我正在处理numpy
documentation on subclassing ndarray
,但在设置默认值方面,我观察到的结果与所描述的结果略有不同。该文档似乎说应该使用一个位置来设置额外属性的默认值,但我发现有必要在两个位置维护正确的默认值。
考虑realistic example文档中进行了描述,转载于此处(为简洁起见,注释已重新格式化):
import numpy as np
class RealisticInfoArray(np.ndarray):
def __new__(cls, input_array, info=None):
obj = np.asarray(input_array).view(cls)
obj.info = info
return obj
def __array_finalize__(self, obj):
# Note that it is here, rather than in the __new__ method,
# that we set the default value for 'info', because this
# method sees all creation of default objects - with the
# InfoArray.__new__ constructor, but also with
# arr.view(InfoArray).
if obj is None: return
self.info = getattr(obj, 'info', None)
描述默认值在何处生效的注释让我感到困惑。我替换了默认值以查看发生了什么,如下所示: __new__
中的 info='michelangelo'
和 getattr(obj, 'info', 'donatello ')
在 __array_finalize__
中。我发现后者仅使用 view
创建方法设置,而前者用于显式构造函数调用和从模板创建:
>>> a = RealisticInfoArray(np.arange(10)); print(a.info)
michelangelo
>>> b = np.arange(10).view(RealisticInfoArray); print(b.info)
donatello
>>> c = a[1:]; print(c.info)
michelangelo
在这种情况下,根据我对文档的阅读,看起来 a
采用了“错误”的默认值。据我所知,a.info
在__array_finalize__
中设置正确,但随后被__new__
中的默认值覆盖。
首先,我是不是搞错了?如果不是,我是否需要在两个地方都保持预期的默认值,或者有没有办法将其减少到一个? (我在下面的回答中对此进行了尝试,欢迎任何反馈。)
最佳答案
我当前的解决方法(除了维护两个默认值之外)是提供一个类属性 DEFAULT_INFO
,该属性可以由 __new__
和 访问>__array_finalize__
:
import numpy as np
class RealisticInfoArray(np.ndarray):
DEFAULT_INFO = 'michelangelo'
def __new__(cls, input_array, info=None):
obj = np.asarray(input_array).view(cls)
if info is None:
info = obj.DEFAULT_INFO
obj.info = info
return obj
def __array_finalize__(self, obj):
if obj is None: return
self.info = getattr(obj, 'info', self.DEFAULT_INFO)
正确产生:
>>> a = RealisticInfoArray(np.arange(10)); print(a.info)
michelangelo
>>> b = np.arange(10).view(RealisticInfoArray); print(b.info)
michelangelo
>>> c = a[1:]; print(c.info)
michelangelo
这还有一个额外的好处,即该类的用户可以修改默认值(例如RealisticInfoArray.DEFAULT_INFO = 'leonardo'
),并使用他们自己认为合适的默认值。但是,我尚未测试此解决方案是否会产生意外副作用。
关于python - 了解 `np.ndarray` 子类化中的默认值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53875001/