NumPy基础知识(九)
子类ndarray
简介
ndarray的子类化相对简单,但是与其他Python对象相比,它具有一些复杂性。在此页面上,我们解释了允许您对ndarray进行子类化的机制,以及实现子类的含义。
ndarrays和对象创建
ndarray的子类化很复杂,因为ndarray类的新实例可以以三种不同的方式出现。这些是:
-
显式构造函数调用-如中所述
MySubClass(params)
。这是创建Python实例的常用方法。 -
视图转换-将现有的ndarray转换为给定的子类
-
模板新功能-从模板实例创建新实例。示例包括从子类数组返回切片,从ufunc创建返回类型以及复制数组。有关更多详细信息,请参见 从模板创建新内容。
最后两个是ndarray的特征-为了支持数组切片之类的事情。ndarray子类化的复杂性归因于numpy必须支持后两种实例创建路径的机制。
查看铸造
视图转换是标准的ndarray机制,通过该机制,您可以获取任何子类的ndarray,并将该数组的视图作为另一个(指定的)子类返回:
>>> import numpy as np
>>> # create a completely useless ndarray subclass
>>> class C(np.ndarray): pass
>>> # create a standard ndarray
>>> arr = np.zeros((3,))
>>> # take a view of it, as our useless subclass
>>> c_arr = arr.view(C)
>>> type(c_arr)
<class 'C'>
从模板创建新的
当numpy发现需要从模板实例创建新实例时,ndarray子类的新实例也可以通过与View Cast非常相似的机制来实现。这最明显的地方是当您获取子类数组的切片时。例如:
>>> v = c_arr[1:]
>>> type(v) # the view is of type 'C'
<class 'C'>
>>> v is c_arr # but it's a new instance
False
切片是原始数据的视图c_arr
。因此,当我们从ndarray进行查看时,我们将返回一个相同类的新ndarray,它指向原始数据。
在使用ndarray时,还有其他需要我们提供此类视图的要点,例如复制数组(c_arr.copy()
),创建ufunc输出数组(有关ufuncs和其他函数,另请参见__array_wrap__)以及简化方法(如 c_arr.mean()
)。
视图转换和新模板的关系
这些路径都使用相同的机器。我们在这里有所不同,因为它们导致您的方法输入不同。具体来说, 视图转换意味着您已经从ndarray的任何潜在子类中创建了数组类型的新实例。 通过模板创建new 意味着您已经从现有实例创建了类的新实例,例如,允许您跨子类特有的属性进行复制。
子类化的含义
如果我们将ndarray子类化,则不仅需要处理数组类型的显式构造,还需要处理View铸造或 从template创建new。NumPy拥有执行此操作的机制,并且这种使子类化成为稍微不标准的机制。
ndarray用于支持子类中的视图和new-from-template的机制有两个方面。
首先是使用该ndarray.__new__
方法进行对象初始化的主要工作,而不是使用更常用的__init__
方法。第二个方法是使用该__array_finalize__
方法允许子类在从模板创建视图和新实例之后进行清理。
在一个简短的Python底漆__new__
和__init__
__new__
是标准的Python方法,如果存在,则在__init__
创建类实例之前被调用。有关更多详细信息,请参见python __new__文档。
例如,考虑以下Python代码:
class C:
def __new__(cls, *args):
print('Cls in __new__:', cls)
print('Args in __new__:', args)
# The `object` type __new__ method takes a single argument.
return object.__new__(cls)
def __init__(self, *args):
print('type(self) in __init__:', type(self))
print('Args in __init__:', args)
意味着我们得到:
>>> c = C('hello')
Cls in __new__: <class 'C'>
Args in __new__: ('hello',)
type(self) in __init__: <class 'C'>
Args in __init__: ('hello',)
当我们调用时C('hello')
,该__new__
方法将其自身的类作为第一个参数,并将传递的参数作为字符串 'hello'
。在python调用之后__new__
,它通常(如下所示)调用我们的__init__
方法,其输出__new__
作为第一个参数(现在是一个类实例),然后是传递的参数。
如您所见,对象可以在__new__
方法中或在__init__
方法中或在这两者中初始化,实际上ndarray没有__init__
方法,因为所有初始化都在__new__
方法中完成。
为什么要使用__new__
而不是仅仅使用普通的__init__
?因为在某些情况下,对于ndarray,我们希望能够返回某个其他类的对象。考虑以下:
class D(C):
def __new__(cls, *args):
print('D cls is:', cls)
print('D args in __new__:', args)
return C.__new__(C, *args)
def __init__(