10000 ENH: Let ndarray.__array_finalize__ be callable. · numpy/numpy@b375f4e · GitHub
[go: up one dir, main page]

Skip to content

Commit b375f4e

Browse files
committed
ENH: Let ndarray.__array_finalize__ be callable.
This helps subclasses, who can now do super() in their own implementation.
1 parent f4a3e07 commit b375f4e

File tree

6 files changed

+38
-25
lines changed

6 files changed

+38
-25
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
``ndarray.__array_finalize__`` is now callable
2+
----------------------------------------------
3+
This means subclasses can now use ``super().__array_finalize__(obj)``
4+
without worrying whether ``ndarray`` is their superclass or not.
5+
The actual call remains a no-op.

numpy/core/_add_newdocs.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2265,10 +2265,6 @@
22652265
"""Array protocol: Python side."""))
22662266

22672267

2268-
add_newdoc('numpy.core.multiarray', 'ndarray', ('__array_finalize__',
2269-
"""None."""))
2270-
2271-
22722268
add_newdoc('numpy.core.multiarray', 'ndarray', ('__array_priority__',
22732269
"""Array priority."""))
22742270

@@ -2278,12 +2274,12 @@
22782274

22792275
add_newdoc('numpy.core.multiarray', 'ndarray', ('__dlpack__',
22802276
"""a.__dlpack__(*, stream=None)
2281-
2277+
22822278
DLPack Protocol: Part of the Array API."""))
22832279

22842280
add_newdoc('numpy.core.multiarray', 'ndarray', ('__dlpack_device__',
22852281
"""a.__dlpack_device__()
2286-
2282+
22872283
DLPack Protocol: Part of the Array API."""))
22882284

22892285
add_newdoc('numpy.core.multiarray', 'ndarray', ('base',
@@ -2811,6 +2807,14 @@
28112807
"""))
28122808

28132809

2810+
add_newdoc('numpy.core.multiarray', 'ndarray', ('__array_finalize__',
2811+
"""a.__array_finalize__(obj, /)
2812+
2813+
Present so subclasses can call super. Does nothing.
2814+
2815+
"""))
2816+
2817+
28142818
add_newdoc('numpy.core.multiarray', 'ndarray', ('__array_prepare__',
28152819
"""a.__array_prepare__(array[, context], /)
28162820

numpy/core/src/multiarray/getset.c

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -926,15 +926,6 @@ array_transpose_get(PyArrayObject *self, void *NPY_UNUSED(ignored))
926926
return PyArray_Transpose(self, NULL);
927927
}
928928

929-
/* If this is None, no function call is made
930-
--- default sub-class behavior
931-
*/
932-
static PyObject *
933-
array_finalize_get(PyArrayObject *NPY_UNUSED(self), void *NPY_UNUSED(ignored))
934-
{
935-
Py_RETURN_NONE;
936-
}
937-
938929
NPY_NO_EXPORT PyGetSetDef array_getsetlist[] = {
939930
{"ndim",
940931
(getter)array_ndim_get,
@@ -1008,10 +999,6 @@ NPY_NO_EXPORT PyGetSetDef array_getsetlist[] = {
1008999
(getter)array_priority_get,
10091000
NULL,
10101001
NULL, NULL},
1011-
{"__array_finalize__",
1012-
(getter)array_finalize_get,
1013-
NULL,
1014-
NULL, NULL},
10151002
{NULL, NULL, NULL, NULL, NULL}, /* Sentinel */
10161003
};
10171004

numpy/core/src/multiarray/methods.c

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -859,7 +859,7 @@ array_astype(PyArrayObject *self,
859859
* and it's not a subtype if subok is False, then we
860860
* can skip the copy.
861861
*/
862-
if (forcecopy != NPY_COPY_ALWAYS &&
862+
if (forcecopy != NPY_COPY_ALWAYS &&
863863
(order == NPY_KEEPORDER ||
864864
(order == NPY_ANYORDER &&
865865
(PyArray_IS_C_CONTIGUOUS(self) ||
@@ -881,7 +881,7 @@ array_astype(PyArrayObject *self,
881881
Py_DECREF(dtype);
882882
return NULL;
883883
}
884-
884+
885885
if (!PyArray_CanCastArrayTo(self, dtype, casting)) {
886886
PyErr_Clear();
887887
npy_set_invalid_cast_error(
@@ -925,6 +925,13 @@ array_astype(PyArrayObject *self,
925925
/* default sub-type implementation */
926926

927927

928+
static PyObject *
929+
array_finalizearray(PyArrayObject *self, PyObject *obj)
930+
{
931+
Py_RETURN_NONE;
932+
}
933+
934+
928935
static PyObject *
929936
array_wraparray(PyArrayObject *self, PyObject *args)
930937
{
@@ -2777,6 +2784,9 @@ NPY_NO_EXPORT PyMethodDef array_methods[] = {
27772784
{"__array_prepare__",
27782785
(PyCFunction)array_preparearray,
27792786
METH_VARARGS, NULL},
2787+
{"__array_finalize__",
2788+
(PyCFunction)array_finalizearray,
2789+
METH_O, NULL},
27802790
{"__array_wrap__",
27812791
(PyCFunction)array_wraparray,
27822792
METH_VARARGS, NULL},

numpy/core/tests/test_multiarray.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8997,6 +8997,14 @@ class Dummy: pass
89978997
break_cycles()
89988998
assert_(obj_ref() is None, "no references should remain")
89998999

9000+
def test_can_use_super(self):
9001+
class SuperFinalize(np.ndarray):
9002+
def __array_finalize__(self, obj):
9003+
self.saved_result = super().__array_finalize__(obj)
9004+
9005+
a = np.array(1).view(SuperFinalize)
9006+
assert_(a.saved_result is None)
9007+
90009008

90019009
def test_orderconverter_with_nonASCII_unicode_ordering():
90029010
# gh-7475
@@ -9201,7 +9209,7 @@ def test_smaller_dtype_multiple(self):
92019209
# x is non-contiguous
92029210
x = np.arange(10, dtype='<i4')[::2]
92039211
with pytest.raises(ValueError,
9204-
match='the last axis must be contiguous'):
9212+
match='the last axis must be contiguous'):
92059213
x.view('<i2')
92069214
expected = [[0, 0], [2, 0], [4, 0], [6, 0], [8, 0]]
92079215
assert_array_equal(x[:, np.newaxis].view('<i2'), expected)

numpy/ma/tests/test_subclassing.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,7 @@ def __new__(cls,arr,info={}):
2828
return x
2929

3030
def __array_finalize__(self, obj):
31-
if callable(getattr(super(), '__array_finalize__', None)):
32-
super().__array_finalize__(obj)
31+
super().__array_finalize__(obj)
3332
self.info = getattr(obj, 'info', {}).copy()
3433
return
3534

@@ -315,7 +314,7 @@ def test_subclass_repr(self):
315314
assert_startswith(repr(mx), 'masked_array')
316315
xsub = SubArray(x)
317316
mxsub = masked_array(xsub, mask=[True, False, True, False, False])
318-
assert_startswith(repr(mxsub),
317+
assert_startswith(repr(mxsub),
319318
f'masked_{SubArray.__name__}(data=[--, 1, --, 3, 4]')
320319

321320
def test_subclass_str(self):

0 commit comments

Comments
 (0)
0