Skip to content

Commit 31084ba

Browse files
committed
Issue python#23632: Memoryviews now allow tuple indexing (including for multi-dimensional memoryviews).
1 parent 9eb57c5 commit 31084ba

4 files changed

Lines changed: 195 additions & 51 deletions

File tree

Doc/library/stdtypes.rst

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3282,10 +3282,8 @@ copying.
32823282
the view. The :class:`~memoryview.itemsize` attribute will give you the
32833283
number of bytes in a single element.
32843284

3285-
A :class:`memoryview` supports slicing to expose its data. If
3286-
:class:`~memoryview.format` is one of the native format specifiers
3287-
from the :mod:`struct` module, indexing will return a single element
3288-
with the correct type. Full slicing will result in a subview::
3285+
A :class:`memoryview` supports slicing and indexing to expose its data.
3286+
One-dimensional slicing will result in a subview::
32893287

32903288
>>> v = memoryview(b'abcefg')
32913289
>>> v[1]
@@ -3297,25 +3295,29 @@ copying.
32973295
>>> bytes(v[1:4])
32983296
b'bce'
32993297

3300-
Other native formats::
3298+
If :class:`~memoryview.format` is one of the native format specifiers
3299+
from the :mod:`struct` module, indexing with an integer or a tuple of
3300+
integers is also supported and returns a single *element* with
3301+
the correct type. One-dimensional memoryviews can be indexed
3302+
with an integer or a one-integer tuple. Multi-dimensional memoryviews
3303+
can be indexed with tuples of exactly *ndim* integers where *ndim* is
3304+
the number of dimensions. Zero-dimensional memoryviews can be indexed
3305+
with the empty tuple.
3306+
3307+
Here is an example with a non-byte format::
33013308

33023309
>>> import array
33033310
>>> a = array.array('l', [-11111111, 22222222, -33333333, 44444444])
3304-
>>> a[0]
3311+
>>> m = memoryview(a)
3312+
>>> m[0]
33053313
-11111111
3306-
>>> a[-1]
3314+
>>> m[-1]
33073315
44444444
3308-
>>> a[2:3].tolist()
3309-
[-33333333]
3310-
>>> a[::2].tolist()
3316+
>>> m[::2].tolist()
33113317
[-11111111, -33333333]
3312-
>>> a[::-1].tolist()
3313-
[44444444, -33333333, 22222222, -11111111]
3314-
3315-
.. versionadded:: 3.3
33163318

3317-
If the underlying object is writable, the memoryview supports slice
3318-
assignment. Resizing is not allowed::
3319+
If the underlying object is writable, the memoryview supports
3320+
one-dimensional slice assignment. Resizing is not allowed::
33193321

33203322
>>> data = bytearray(b'abcefg')
33213323
>>> v = memoryview(data)
@@ -3348,12 +3350,16 @@ copying.
33483350
True
33493351

33503352
.. versionchanged:: 3.3
3353+
One-dimensional memoryviews can now be sliced.
33513354
One-dimensional memoryviews with formats 'B', 'b' or 'c' are now hashable.
33523355

33533356
.. versionchanged:: 3.4
33543357
memoryview is now registered automatically with
33553358
:class:`collections.abc.Sequence`
33563359

3360+
.. versionchanged:: 3.5
3361+
memoryviews can now be indexed with tuple of integers.
3362+
33573363
:class:`memoryview` has several methods:
33583364

33593365
.. method:: __eq__(exporter)

Lib/test/test_buffer.py

Lines changed: 50 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
# memoryview tests is now in this module.
1212
#
1313

14+
import contextlib
1415
import unittest
1516
from test import support
1617
from itertools import permutations, product
@@ -2825,6 +2826,13 @@ def test_memoryview_sequence(self):
28252826
m = memoryview(ex)
28262827
self.assertRaises(TypeError, eval, "9.0 in m", locals())
28272828

2829+
@contextlib.contextmanager
2830+
def assert_out_of_bounds_error(self, dim):
2831+
with self.assertRaises(IndexError) as cm:
2832+
yield
2833+
self.assertEqual(str(cm.exception),
2834+
"index out of bounds on dimension %d" % (dim,))
2835+
28282836
def test_memoryview_index(self):
28292837

28302838
# ndim = 0
@@ -2851,12 +2859,31 @@ def test_memoryview_index(self):
28512859
self.assertRaises(IndexError, m.__getitem__, -8)
28522860
self.assertRaises(IndexError, m.__getitem__, 8)
28532861

2854-
# Not implemented: multidimensional sub-views
2862+
# multi-dimensional
28552863
ex = ndarray(list(range(12)), shape=[3,4], flags=ND_WRITABLE)
28562864
m = memoryview(ex)
28572865

2858-
self.assertRaises(NotImplementedError, m.__getitem__, 0)
2859-
self.assertRaises(NotImplementedError, m.__setitem__, 0, 9)
2866+
self.assertEqual(m[0, 0], 0)
2867+
self.assertEqual(m[2, 0], 8)
2868+
self.assertEqual(m[2, 3], 11)
2869+
self.assertEqual(m[-1, -1], 11)
2870+
self.assertEqual(m[-3, -4], 0)
2871+
2872+
# out of bounds
2873+
for index in (3, -4):
2874+
with self.assert_out_of_bounds_error(dim=1):
2875+
m[index, 0]
2876+
for index in (4, -5):
2877+
with self.assert_out_of_bounds_error(dim=2):
2878+
m[0, index]
2879+
self.assertRaises(IndexError, m.__getitem__, (2**64, 0))
2880+
self.assertRaises(IndexError, m.__getitem__, (0, 2**64))
2881+
2882+
self.assertRaises(TypeError, m.__getitem__, (0, 0, 0))
2883+
self.assertRaises(TypeError, m.__getitem__, (0.0, 0.0))
2884+
2885+
# Not implemented: multidimensional sub-views
2886+
self.assertRaises(NotImplementedError, m.__getitem__, ())
28602887
self.assertRaises(NotImplementedError, m.__getitem__, 0)
28612888

28622889
def test_memoryview_assign(self):
@@ -2945,10 +2972,27 @@ def test_memoryview_assign(self):
29452972
m = memoryview(ex)
29462973
self.assertRaises(NotImplementedError, m.__setitem__, 0, 1)
29472974

2948-
# Not implemented: multidimensional sub-views
2975+
# multi-dimensional
29492976
ex = ndarray(list(range(12)), shape=[3,4], flags=ND_WRITABLE)
29502977
m = memoryview(ex)
2978+
m[0,1] = 42
2979+
self.assertEqual(ex[0][1], 42)
2980+
m[-1,-1] = 43
2981+
self.assertEqual(ex[2][3], 43)
2982+
# errors
2983+
for index in (3, -4):
2984+
with self.assert_out_of_bounds_error(dim=1):
2985+
m[index, 0] = 0
2986+
for index in (4, -5):
2987+
with self.assert_out_of_bounds_error(dim=2):
2988+
m[0, index] = 0
2989+
self.assertRaises(IndexError, m.__setitem__, (2**64, 0), 0)
2990+
self.assertRaises(IndexError, m.__setitem__, (0, 2**64), 0)
2991+
2992+
self.assertRaises(TypeError, m.__setitem__, (0, 0, 0), 0)
2993+
self.assertRaises(TypeError, m.__setitem__, (0.0, 0.0), 0)
29512994

2995+
# Not implemented: multidimensional sub-views
29522996
self.assertRaises(NotImplementedError, m.__setitem__, 0, [2, 3])
29532997

29542998
def test_memoryview_slice(self):
@@ -2961,8 +3005,8 @@ def test_memoryview_slice(self):
29613005
self.assertRaises(ValueError, m.__setitem__, slice(0,2,0),
29623006
bytearray([1,2]))
29633007

2964-
# invalid slice key
2965-
self.assertRaises(TypeError, m.__getitem__, ())
3008+
# 0-dim slicing (identity function)
3009+
self.assertRaises(NotImplementedError, m.__getitem__, ())
29663010

29673011
# multidimensional slices
29683012
ex = ndarray(list(range(12)), shape=[12], flags=ND_WRITABLE)

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ Release date: 2015-03-28
1010
Core and Builtins
1111
-----------------
1212

13+
- Issue #23632: Memoryviews now allow tuple indexing (including for
14+
multi-dimensional memoryviews).
15+
1316
- Issue #23192: Fixed generator lambdas. Patch by Bruno Cauet.
1417

1518
- Issue #23629: Fix the default __sizeof__ implementation for variable-sized

0 commit comments

Comments
 (0)