10000 MAINT: Deprecate `.T` property for non-2dim arrays and scalars · numpy/numpy@bc88f60 · GitHub
[go: up one dir, main page]

Skip to content

Commit bc88f60

Browse files
committed
MAINT: Deprecate .T property for non-2dim arrays and scalars
1 parent d840358 commit bc88f60

17 files changed

+94
-64
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
* ``arr.T`` property has been deprecated for array scalars and arrays with
2+
dimensionality different than ``2``.

numpy/_core/_add_newdocs.py

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2887,7 +2887,8 @@
28872887
"""
28882888
View of the transposed array.
28892889
2890-
Same as ``self.transpose()``.
2890+
Same as ``self.transpose()`` except that it requires
2891+
the array to be 2-dimensional.
28912892
28922893
Examples
28932894
--------
@@ -2900,12 +2901,6 @@
29002901
array([[1, 3],
29012902
[2, 4]])
29022903
2903-
>>> a = np.array([1, 2, 3, 4])
2904-
>>> a
2905-
array([1, 2, 3, 4])
2906-
>>> a.T
2907-
array([1, 2, 3, 4])
2908-
29092904
See Also
29102905
--------
29112906
transpose

numpy/_core/src/multiarray/getset.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -848,6 +848,15 @@ array_flat_set(PyArrayObject *self, PyObject *val, void *NPY_UNUSED(ignored))
848848
static PyObject *
849849
array_transpose_get(PyArrayObject *self, void *NPY_UNUSED(ignored))
850850
{
851+
int ndim = PyArray_NDIM(self);
852+
if (ndim != 2) {
853+
if (PyErr_WarnFormat(PyExc_UserWarning, 1,
854+
"In the future `.T` property will be supported for "
855+
"2-dim arrays only. Here it is %d-dim array.",
856+
ndim) < 0) {
857+
return NULL;
858+
}
859+
}
851860
return PyArray_Transpose(self, NULL);
852861
}
853862

numpy/_core/src/multiarray/scalartypes.c.src

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1925,6 +1925,11 @@ gentype_flat_get(PyObject *self, void *NPY_UNUSED(ignored))
19251925
static PyObject *
19261926
gentype_transpose_get(PyObject *self, void *NPY_UNUSED(ignored))
19271927
{
1928+
if (PyErr_WarnEx(PyExc_UserWarning,
1929+
"In the future `.T` property for array scalars will "
1930+
"raise an error.", 1) < 0) {
1931+
return NULL;
1932+
}
19281933
Py_INCREF(self);
19291934
return self;
19301935
}

numpy/_core/tests/test_deprecations.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,3 +452,17 @@ def test_deprecated(self):
452452
struct_ufunc.add_triplet, "new docs"
453453
)
454454
)
455+
456+
457+
def test_deprecated_T_non_2dim():
458+
# Deprecated in Numpy 2.3, 2025-04
459+
with pytest.warns(UserWarning, match="In the future `.T` property for "
460+
"array scalars will raise an error."):
461+
np.int64(1).T
462+
for shape in [(5,), (2, 3, 4)]:
463+
with pytest.warns(
464+
UserWarning,
465+
match="In the future `.T` property will be "
466+
"supported for 2-dim arrays only. "
467+
f"Here it is {len(shape)}-dim array."):
468+
np.ones(shape).T

numpy/_core/tests/test_einsum.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -441,9 +441,9 @@ def check_einsum_sums(self, dtype, do_opt=False):
441441
a = np.arange(n * 3 * 2, dtype=dtype).reshape(n, 3, 2)
442442
b = np.arange(n, dtype=dtype)
443443
assert_equal(np.einsum("i..., i...", a, b, optimize=do_opt),
444-
np.inner(a.T, b.T).T)
444+
np.inner(a.transpose(), b.transpose()).transpose())
445445
assert_equal(np.einsum(a, [0, Ellipsis], b, [0, Ellipsis], optimize=do_opt),
446-
np.inner(a.T, b.T).T)
446+
np.inner(a.transpose(), b.transpose()).transpose())
447447

448448
# outer(a,b)
449449
for n in range(1, 17):
@@ -483,22 +483,22 @@ def check_einsum_sums(self, dtype, do_opt=False):
483483
for n in range(1, 17):
484484
a = np.arange(4 * n, dtype=dtype).reshape(4, n)
485485
b = np.arange(n, dtype=dtype)
486-
assert_equal(np.einsum("ji,j", a.T, b.T, optimize=do_opt),
487-
np.dot(b.T, a.T))
488-
assert_equal(np.einsum(a.T, [1, 0], b.T, [1], optimize=do_opt),
489-
np.dot(b.T, a.T))
486+
assert_equal(np.einsum("ji,j", a.T, b, optimize=do_opt),
487+
np.dot(b, a.T))
488+
assert_equal(np.einsum(a.T, [1, 0], b, [1], optimize=do_opt),
489+
np.dot(b, a.T))
490490

491491
c = np.arange(4, dtype=dtype)
492-
np.einsum("ji,j", a.T, b.T, out=c,
492+
np.einsum("ji,j", a.T, b, out=c,
493493
dtype='f8', casting='unsafe', optimize=do_opt)
494494
assert_equal(c,
495-
np.dot(b.T.astype('f8'),
495+
np.dot(b.astype('f8'),
496496
a.T.astype('f8')).astype(dtype))
497497
c[...] = 0
498-
np.einsum(a.T, [1, 0], b.T, [1], out=c,
498+
np.einsum(a.T, [1, 0], b, [1], out=c,
499499
dtype='f8', casting='unsafe', optimize=do_opt)
500500
assert_equal(c,
501-
np.dot(b.T.astype('f8'),
501+
np.dot(b.astype('f8'),
502502
a.T.astype('f8')).astype(dtype))
503503

504504
# matmat(a,b) / a.dot(b) where a is matrix, b is matrix

numpy/_core/tests/test_indexing.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -331,8 +331,8 @@ def test_uncontiguous_subspace_assignment(self):
331331
a = np.full((3, 4, 2), -1)
332332
b = np.full((3, 4, 2), -1)
333333

334-
a[[0, 1]] = np.arange(2 * 4 * 2).reshape(2, 4, 2).T
335-
b[[0, 1]] = np.arange(2 * 4 * 2).reshape(2, 4, 2).T.copy()
334+
a[[0, 1]] = np.arange(2 * 4 * 2).reshape(2, 4, 2).transpose()
335+
b[[0, 1]] = np.arange(2 * 4 * 2).reshape(2, 4, 2).transpose().copy()
336336

337337
assert_equal(a, b)
338338

numpy/_core/tests/test_multiarray.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3235,7 +3235,7 @@ def test_flatten(self):
32353235
assert_equal(x0.flatten('F'), x0.T.flatten())
32363236
assert_equal(x1.flatten(), y1)
32373237
assert_equal(x1.flatten('F'), y1f)
3238-
assert_equal(x1.flatten('F'), x1.T.flatten())
3238+
assert_equal(x1.flatten('F'), x1.transpose().flatten())
32393239

32403240
@pytest.mark.parametrize('func', (np.dot, np.matmul))
32413241
def test_arr_mult(self, func):
@@ -3340,8 +3340,8 @@ def test_no_dgemv(self, func, dtype):
33403340
ret2 = func(a.copy(), b.copy())
33413341
assert_equal(ret1, ret2)
33423342

3343-
ret1 = func(b.T, a.T)
3344-
ret2 = func(b.T.copy(), a.T.copy())
3343+
ret1 = func(b.transpose(), a.transpose())
3344+
ret2 = func(b.transpose().copy(), a.transpose().copy())
33453345
assert_equal(ret1, ret2)
33463346

33473347
def test_dot(self):
@@ -4678,7 +4678,7 @@ def test_np_argmin_argmax_keepdims(self, size, axis, method):
46784678
wrong_shape[0] = 2
46794679
wrong_outarray = np.empty(wrong_shape, dtype=res.dtype)
46804680
with pytest.raises(ValueError):
4681-
method(arr.T, axis=axis,
4681+
method(arr.transpose(), axis=axis,
46824682
out=wrong_outarray, keepdims=True)
46834683

46844684
# non-contiguous arrays
@@ -4689,14 +4689,14 @@ def test_np_argmin_argmax_keepdims(self, size, axis, method):
46894689
new_shape[axis] = 1
46904690
new_shape = tuple(new_shape)
46914691

4692-
_res_orig = method(arr.T, axis=axis)
4692+
_res_orig = method(arr.transpose(), axis=axis)
46934693
res_orig = _res_orig.reshape(new_shape)
4694-
res = method(arr.T, axis=axis, keepdims=True)
4694+
res = method(arr.transpose(), axis=axis, keepdims=True)
46954695
assert_equal(res, res_orig)
46964696
assert_(res.shape == new_shape)
46974697
outarray = np.empty(new_shape[::-1], dtype=res.dtype)
4698-
outarray = outarray.T
4699-
res1 = method(arr.T, axis=axis, out=outarray,
4698+
outarray = outarray.transpose()
4699+
res1 = method(arr.transpose(), axis=axis, out=outarray,
47004700
keepdims=True)
47014701
assert_(res1 is outarray)
47024702
assert_equal(res, outarray)
@@ -4716,7 +4716,7 @@ def test_np_argmin_argmax_keepdims(self, size, axis, method):
47164716
wrong_shape[0] = 2
47174717
wrong_outarray = np.empty(wrong_shape, dtype=res.dtype)
47184718
with pytest.raises(ValueError):
4719-
method(arr.T, axis=axis,
4719+
method(arr.transpose(), axis=axis,
47204720
out=wrong_outarray, keepdims=True)
47214721

47224722
@pytest.mark.parametrize('method', ['max', 'min'])
@@ -8345,7 +8345,7 @@ def test_relaxed_strides(self, c=np.ones((1, 10, 10), dtype='i8')): # noqa: B00
83458345
fd = io.BytesIO()
83468346
fd.write(c.data)
83478347

8348-
fortran = c.T
8348+
fortran = c.transpose()
83498349
assert_(memoryview(fortran).strides == (8, 80, 800))
83508350

83518351
arr = np.ones((1, 10))

numpy/_core/tests/test_nditer.py

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ def test_iter_best_order():
104104
i = nditer(aview, [], [['readonly']])
105105
assert_equal(list(i), a)
106106
# Fortran-order
107-
i = nditer(aview.T, [], [['readonly']])
107+
i = nditer(aview.transpose(), [], [['readonly']])
108108
assert_equal(list(i), a)
109109
# Other order
110110
if len(shape) > 2:
@@ -130,8 +130,8 @@ def test_iter_c_order():
130130
i = nditer(aview, order='C')
131131
assert_equal(list(i), aview.ravel(order='C'))
132132
# Fortran-order
133-
i = nditer(aview.T, order='C')
134-
assert_equal(list(i), aview.T.ravel(order='C'))
133+
i = nditer(aview.transpose(), order='C')
134+
assert_equal(list(i), aview.transpose().ravel(order='C'))
135135
# Other order
136136
if len(shape) > 2:
137137
i = nditer(aview.swapaxes(0, 1), order='C')
@@ -157,8 +157,8 @@ def test_iter_f_order():
157157
i = nditer(aview, order='F')
158158
assert_equal(list(i), aview.ravel(order='F'))
159159
# Fortran-order
160-
i = nditer(aview.T, order='F')
161-
assert_equal(list(i), aview.T.ravel(order='F'))
160+
i = nditer(aview.transpose(), order='F')
161+
assert_equal(list(i), aview.transpose().ravel(order='F'))
162162
# Other order
163163
if len(shape) > 2:
164164
i = nditer(aview.swapaxes(0, 1), order='F')
@@ -184,8 +184,8 @@ def test_iter_c_or_f_order():
184184
i = nditer(aview, order='A')
185185
assert_equal(list(i), aview.ravel(order='A'))
186186
# Fortran-order
187-
i = nditer(aview.T, order='A')
188-
assert_equal(list(i), aview.T.ravel(order='A'))
187+
i = nditer(aview.transpose(), order='A')
188+
assert_equal(list(i), aview.transpose().ravel(order='A'))
189189
# Other order
190190
if len(shape) > 2:
191191
i = nditer(aview.swapaxes(0, 1), order='A')
@@ -471,7 +471,7 @@ def test_iter_no_inner_full_coalesce():
471471
assert_equal(i.ndim, 1)
472472
assert_equal(i[0].shape, (size,))
473473
# Fortran-order
474-
i = nditer(aview.T, ['external_loop'], [['readonly']])
474+
i = nditer(aview.transpose(), ['external_loop'], [['readonly']])
475475
assert_equal(i.ndim, 1)
476476
assert_equal(i[0].shape, (size,))
477477
# Other order
@@ -519,26 +519,26 @@ def test_iter_dim_coalescing():
519519
assert_equal(i.ndim, 1)
520520
i = nditer(a3d.swapaxes(0, 1), ['c_index'], [['readonly']])
521521
assert_equal(i.ndim, 3)
522-
i = nditer(a3d.T, ['c_index'], [['readonly']])
522+
i = nditer(a3d.transpose(), ['c_index'], [['readonly']])
523523
assert_equal(i.ndim, 3)
524-
i = nditer(a3d.T, ['f_index'], [['readonly']])
524+
i = nditer(a3d.transpose(), ['f_index'], [['readonly']])
525525
assert_equal(i.ndim, 1)
526-
i = nditer(a3d.T.swapaxes(0, 1), ['f_index'], [['readonly']])
526+
i = nditer(a3d.transpose().swapaxes(0, 1), ['f_index'], [['readonly']])
527527
assert_equal(i.ndim, 3)
528528

529529
# When C or F order is forced, coalescing may still occur
530530
a3d = arange(24).reshape(2, 3, 4)
531531
i = nditer(a3d, order='C')
532532
assert_equal(i.ndim, 1)
533-
i = nditer(a3d.T, order='C')
533+
i = nditer(a3d.transpose(), order='C')
534534
assert_equal(i.ndim, 3)
535535
i = nditer(a3d, order='F')
536536
assert_equal(i.ndim, 3)
537-
i = nditer(a3d.T, order='F')
537+
i = nditer(a3d.transpose(), order='F')
538538
assert_equal(i.ndim, 1)
539539
i = nditer(a3d, order='A')
540540
assert_equal(i.ndim, 1)
541-
i = nditer(a3d.T, order='A')
541+
i = nditer(a3d.transpose(), order='A')
542542
assert_equal(i.ndim, 1)
543543

544544
def test_iter_broadcasting():
@@ -804,7 +804,7 @@ def test_iter_slice():
804804
assert_equal(i[0:2], [3, 12])
805805

806806
def test_iter_assign_mapping():
807-
a = np.arange(24, dtype='f8').reshape(2, 3, 4).T
807+
a = np.arange(24, dtype='f8').reshape(2, 3, 4).transpose()
808808
it = np.nditer(a, [], [['readwrite', 'updateifcopy']],
809809
casting='same_kind', op_dtypes=[np.dtype('f4')])
810810
with it:
@@ -923,7 +923,7 @@ def test_iter_array_cast():
923923
assert_equal(i.operands[0].strides, (96, 8, 32))
924924

925925
# Same-kind cast 'f8' -> 'f4' -> 'f8'
926-
a = np.arange(24, dtype='f8').reshape(2, 3, 4).T
926+
a = np.arange(24, dtype='f8').reshape(2, 3, 4).transpose()
927927
with nditer(a, [],
928928
[['readwrite', 'updateifcopy']],
929929
casting='same_kind',
@@ -1297,7 +1297,8 @@ def test_iter_op_axes():
12971297
i = nditer([a, a.T], [], [['readonly']] * 2, op_axes=[[0, 1], [1, 0]])
12981298
assert_(all([x 10000 == y for (x, y) in i]))
12991299
a = arange(24).reshape(2, 3, 4)
1300-
i = nditer([a.T, a], [], [['readonly']] * 2, op_axes=[[2, 1, 0], None])
1300+
i = nditer([a.transpose(), a], [], [['readonly']] * 2,
1301+
op_axes=[[2, 1, 0], None])
13011302
assert_(all([x == y for (x, y) in i]))
13021303

13031304
# Broadcast 1D to any dimension
@@ -1532,7 +1533,7 @@ def test_iter_allocate_output_itorder():
15321533
assert_equal(i.operands[1].strides, a.strides)
15331534
assert_equal(i.operands[1].dtype, np.dtype('f4'))
15341535
# F-order input, best iteration order
1535-
a = arange(24, dtype='i4').reshape(2, 3, 4).T
1536+
a = arange(24, dtype='i4').reshape(2, 3, 4).transpose()
15361537
i = nditer([a, None], [], [['readonly'], ['writeonly', 'allocate']],
15371538
op_dtypes=[None, np.dtype('f4')])
15381539
assert_equal(i.operands[1].shape, a.shape)
@@ -1796,7 +1797,7 @@ def test_iter_buffering():
17961797
# Test buffering with several buffer sizes and types
17971798
arrays = []
17981799
# F-order swapped array
1799-
_tmp = np.arange(24, dtype='c16').reshape(2, 3, 4).T
1800+
_tmp = np.arange(24, dtype='c16').reshape(2, 3, 4).transpose()
18001801
_tmp = _tmp.view(_tmp.dtype.newbyteorder()).byteswap()
18011802
arrays.append(_tmp)
18021803
# Contiguous 1-dimensional array
@@ -1807,7 +1808,7 @@ def test_iter_buffering():
18071808
a[:] = np.arange(16, dtype='i4')
18081809
arrays.append(a)
18091810
# 4-D F-order array
1810-
arrays.append(np.arange(120, dtype='i4').reshape(5, 3, 2, 4).T)
1811+
arrays.append(np.arange(120, dtype='i4').reshape(5, 3, 2, 4).transpose())
18111812
for a in arrays:
18121813
for buffersize in (1, 2, 3, 5, 8, 11, 16, 1024):
18131814
vals = []
@@ -1826,7 +1827,7 @@ def test_iter_write_buffering():
18261827
# Test that buffering of writes is working
18271828

18281829
# F-order swapped array
1829-
a = np.arange(24).reshape(2, 3, 4).T
1830+
a = np.arange(24).reshape(2, 3, 4).transpose()
18301831
a = a.view(a.dtype.newbyteorder()).byteswap()
18311832
i = nditer(a, ['buffered'],
18321833
[['readwrite', 'nbo', 'aligned']],

numpy/_core/tests/test_regression.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -887,7 +887,7 @@ def test_string_sort_with_zeros(self):
887887

888888
def test_copy_detection_zero_dim(self):
889889
# Ticket #658
890-
np.indices((0, 3, 4)).T.reshape(-1, 3)
890+
np.indices((0, 3, 4)).transpose().reshape(-1, 3)
891891

892892
def test_flat_byteorder(self):
893893
# Ticket #657
@@ -906,7 +906,7 @@ def test_flat_index_byteswap(self):
906906

907907
def test_copy_detection_corner_case(self):
908908
# Ticket #658
909-
np.indices((0, 3, 4)).T.reshape(-1, 3)
909+
np.indices((0, 3, 4)).transpose().reshape(-1, 3)
910910

911911
def test_object_array_refcounting(self):
912912
# Ticket #633

numpy/_core/tests/test_shape_base.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,10 @@ def test_concatenate(self):
358358
a2 = res[..., 6:]
359359
assert_array_equal(concatenate((a0, a1, a2), 2), res)
360360
assert_array_equal(concatenate((a0, a1, a2), -1), res)
361-
assert_array_equal(concatenate((a0.T, a1.T, a2.T), 0), res.T)
361+
assert_array_equal(
362+
concatenate((a0.transpose(), a1.transpose(), a2.transpose()), 0),
363+
res.transpose(),
364+
)
362365

363366
out = res.copy()
364367
rout = concatenate((a0, a1, a2), 2, out=out)

numpy/_core/tests/test_ufunc.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1037,8 +1037,8 @@ def test_incontiguous_array(self):
10371037
assert_equal(x[0, 0, 0, 0, 0, 0], -1, err_msg=msg2)
10381038
assert_array_equal(np.vecdot(a, b), np.sum(a * b, axis=-1), err_msg=msg)
10391039
x = np.arange(24).reshape(2, 3, 4)
1040-
a = x.T
1041-
b = x.T
1040+
a = x.transpose()
1041+
b = x.transpose()
10421042
a[0, 0, 0] = -1
10431043
assert_equal(x[0, 0, 0], -1, err_msg=msg2)
10441044
assert_array_equal(np.vecdot(a, b), np.sum(a * b, axis=-1), err_msg=msg)

numpy/fft/tests/test_pocketfft.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,7 @@ def test_all_1d_norm_preserving(self):
413413
def test_fftn_out_argument(self, dtype, transpose, axes):
414414
def zeros_like(x):
415415
if transpose:
416-
return np.zeros_like(x.T).T
416+
return np.zeros_like(x.transpose()).transpose()
417417
else:
418418
return np.zeros_like(x)
419419

numpy/lib/_function_base_impl.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2771,7 +2771,7 @@ def cov(m, y=None, rowvar=True, bias=False, ddof=None, fweights=None,
27712771
>>> v1 = np.sum(w)
27722772
>>> v2 = np.sum(w * a)
27732773
>>> m -= np.sum(m * w, axis=None, keepdims=True) / v1
2774-
>>> cov = np.dot(m * w, m.T) * v1 / (v1**2 - ddof * v2)
2774+
>>> cov = np.dot(m * w, m) * v1 / (v1**2 - ddof * v2)
27752775
27762776
Note that when ``a == 1``, the normalization factor
27772777
``v1 / (v1**2 - ddof * v2)`` goes over to ``1 / (np.sum(f) - ddof)``

0 commit comments

Comments
 (0)
0