Skip to content

Commit

Permalink
pythongh-125420: implement Sequence.count API on memoryview objec…
Browse files Browse the repository at this point in the history
  • Loading branch information
picnixz authored Dec 10, 2024
1 parent 050d59b commit 4331832
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 2 deletions.
8 changes: 7 additions & 1 deletion Doc/library/stdtypes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4149,7 +4149,13 @@ copying.
.. versionchanged:: 3.5
The source format is no longer restricted when casting to a byte view.

.. method:: index(value, start=0, stop=sys.maxsize, /)
.. method:: count(value, /)

Count the number of occurrences of *value*.

.. versionadded:: next

.. method:: index(value, start=0, stop=sys.maxsize, /)

Return the index of the first occurrence of *value* (at or after
index *start* and before index *stop*).
Expand Down
28 changes: 28 additions & 0 deletions Lib/test/test_memoryview.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,22 @@ def test_iter(self):
m = self._view(b)
self.assertEqual(list(m), [m[i] for i in range(len(m))])

def test_count(self):
for tp in self._types:
b = tp(self._source)
m = self._view(b)
l = m.tolist()
for ch in list(m):
self.assertEqual(m.count(ch), l.count(ch))

b = tp((b'a' * 5) + (b'c' * 3))
m = self._view(b) # may be sliced
l = m.tolist()
with self.subTest('count', buffer=b):
self.assertEqual(m.count(ord('a')), l.count(ord('a')))
self.assertEqual(m.count(ord('b')), l.count(ord('b')))
self.assertEqual(m.count(ord('c')), l.count(ord('c')))

def test_setitem_readonly(self):
if not self.ro_type:
self.skipTest("no read-only type to test")
Expand Down Expand Up @@ -464,6 +480,18 @@ def _view(self, obj):
def _check_contents(self, tp, obj, contents):
self.assertEqual(obj, tp(contents))

def test_count(self):
super().test_count()
for tp in self._types:
b = tp((b'a' * 5) + (b'c' * 3))
m = self._view(b) # should not be sliced
self.assertEqual(len(b), len(m))
with self.subTest('count', buffer=b):
self.assertEqual(m.count(ord('a')), 5)
self.assertEqual(m.count(ord('b')), 0)
self.assertEqual(m.count(ord('c')), 3)


class BaseMemorySliceTests:
source_bytes = b"XabcdefY"

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Add :meth:`memoryview.count` to :class:`memoryview` objects. Patch by
Bénédikt Tran.
11 changes: 10 additions & 1 deletion Objects/clinic/memoryobject.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

50 changes: 50 additions & 0 deletions Objects/memoryobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -2748,6 +2748,55 @@ static PySequenceMethods memory_as_sequence = {
};


/****************************************************************************/
/* Counting */
/****************************************************************************/

/*[clinic input]
memoryview.count
value: object
/
Count the number of occurrences of a value.
[clinic start generated code]*/

static PyObject *
memoryview_count(PyMemoryViewObject *self, PyObject *value)
/*[clinic end generated code: output=e2c255a8d54eaa12 input=e3036ce1ed7d1823]*/
{
PyObject *iter = PyObject_GetIter(_PyObject_CAST(self));
if (iter == NULL) {
return NULL;
}

Py_ssize_t count = 0;
PyObject *item = NULL;
while (PyIter_NextItem(iter, &item)) {
if (item == NULL) {
Py_DECREF(iter);
return NULL;
}
if (item == value) {
Py_DECREF(item);
count++; // no overflow since count <= len(mv) <= PY_SSIZE_T_MAX
continue;
}
int contained = PyObject_RichCompareBool(item, value, Py_EQ);
Py_DECREF(item);
if (contained > 0) { // more likely than 'contained < 0'
count++; // no overflow since count <= len(mv) <= PY_SSIZE_T_MAX
}
else if (contained < 0) {
Py_DECREF(iter);
return NULL;
}
}
Py_DECREF(iter);
return PyLong_FromSsize_t(count);
}


/**************************************************************************/
/* Lookup */
/**************************************************************************/
Expand Down Expand Up @@ -3370,6 +3419,7 @@ static PyMethodDef memory_methods[] = {
MEMORYVIEW_CAST_METHODDEF
MEMORYVIEW_TOREADONLY_METHODDEF
MEMORYVIEW__FROM_FLAGS_METHODDEF
MEMORYVIEW_COUNT_METHODDEF
MEMORYVIEW_INDEX_METHODDEF
{"__enter__", memory_enter, METH_NOARGS, NULL},
{"__exit__", memory_exit, METH_VARARGS, memory_exit_doc},
Expand Down

0 comments on commit 4331832

Please sign in to comment.