Issue 23704: Add index(), copy(), and insert() to deques. Register deques as a MutableSequence.
This commit is contained in:
parent
0a9e2721fa
commit
32ea16577d
|
@ -437,6 +437,13 @@ or subtracting from an empty counter.
|
||||||
Remove all elements from the deque leaving it with length 0.
|
Remove all elements from the deque leaving it with length 0.
|
||||||
|
|
||||||
|
|
||||||
|
.. method:: copy()
|
||||||
|
|
||||||
|
Create a shallow copy of the deque.
|
||||||
|
|
||||||
|
.. versionadded:: 3.5
|
||||||
|
|
||||||
|
|
||||||
.. method:: count(x)
|
.. method:: count(x)
|
||||||
|
|
||||||
Count the number of deque elements equal to *x*.
|
Count the number of deque elements equal to *x*.
|
||||||
|
@ -457,6 +464,21 @@ or subtracting from an empty counter.
|
||||||
elements in the iterable argument.
|
elements in the iterable argument.
|
||||||
|
|
||||||
|
|
||||||
|
.. method:: index(x[, start[, end]])
|
||||||
|
|
||||||
|
Return the position of *x* in the deque. Returns the first match
|
||||||
|
or raises :exc:`ValueError` if not found.
|
||||||
|
|
||||||
|
.. versionadded:: 3.5
|
||||||
|
|
||||||
|
|
||||||
|
.. method:: insert(i, x)
|
||||||
|
|
||||||
|
Insert *x* into the deque at position *i*.
|
||||||
|
|
||||||
|
.. versionadded:: 3.5
|
||||||
|
|
||||||
|
|
||||||
.. method:: pop()
|
.. method:: pop()
|
||||||
|
|
||||||
Remove and return an element from the right side of the deque. If no
|
Remove and return an element from the right side of the deque. If no
|
||||||
|
|
|
@ -16,6 +16,8 @@ from _weakref import proxy as _proxy
|
||||||
from itertools import repeat as _repeat, chain as _chain, starmap as _starmap
|
from itertools import repeat as _repeat, chain as _chain, starmap as _starmap
|
||||||
from reprlib import recursive_repr as _recursive_repr
|
from reprlib import recursive_repr as _recursive_repr
|
||||||
|
|
||||||
|
MutableSequence.register(deque)
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
### OrderedDict
|
### OrderedDict
|
||||||
################################################################################
|
################################################################################
|
||||||
|
|
|
@ -13,6 +13,7 @@ import re
|
||||||
import sys
|
import sys
|
||||||
from collections import UserDict
|
from collections import UserDict
|
||||||
from collections import ChainMap
|
from collections import ChainMap
|
||||||
|
from collections import deque
|
||||||
from collections.abc import Hashable, Iterable, Iterator
|
from collections.abc import Hashable, Iterable, Iterator
|
||||||
from collections.abc import Sized, Container, Callable
|
from collections.abc import Sized, Container, Callable
|
||||||
from collections.abc import Set, MutableSet
|
from collections.abc import Set, MutableSet
|
||||||
|
@ -1014,7 +1015,7 @@ class TestCollectionABCs(ABCTestCase):
|
||||||
for sample in [tuple, str, bytes]:
|
for sample in [tuple, str, bytes]:
|
||||||
self.assertNotIsInstance(sample(), MutableSequence)
|
self.assertNotIsInstance(sample(), MutableSequence)
|
||||||
self.assertFalse(issubclass(sample, MutableSequence))
|
self.assertFalse(issubclass(sample, MutableSequence))
|
||||||
for sample in [list, bytearray]:
|
for sample in [list, bytearray, deque]:
|
||||||
self.assertIsInstance(sample(), MutableSequence)
|
self.assertIsInstance(sample(), MutableSequence)
|
||||||
self.assertTrue(issubclass(sample, MutableSequence))
|
self.assertTrue(issubclass(sample, MutableSequence))
|
||||||
self.assertFalse(issubclass(str, MutableSequence))
|
self.assertFalse(issubclass(str, MutableSequence))
|
||||||
|
|
|
@ -231,6 +231,54 @@ class TestBasic(unittest.TestCase):
|
||||||
self.assertRaises(IndexError, d.__getitem__, 0)
|
self.assertRaises(IndexError, d.__getitem__, 0)
|
||||||
self.assertRaises(IndexError, d.__getitem__, -1)
|
self.assertRaises(IndexError, d.__getitem__, -1)
|
||||||
|
|
||||||
|
def test_index(self):
|
||||||
|
for n in 1, 2, 30, 40, 200:
|
||||||
|
|
||||||
|
d = deque(range(n))
|
||||||
|
for i in range(n):
|
||||||
|
self.assertEqual(d.index(i), i)
|
||||||
|
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
d.index(n+1)
|
||||||
|
|
||||||
|
# Test detection of mutation during iteration
|
||||||
|
d = deque(range(n))
|
||||||
|
d[n//2] = MutateCmp(d, False)
|
||||||
|
with self.assertRaises(RuntimeError):
|
||||||
|
d.index(n)
|
||||||
|
|
||||||
|
# Test detection of comparison exceptions
|
||||||
|
d = deque(range(n))
|
||||||
|
d[n//2] = BadCmp()
|
||||||
|
with self.assertRaises(RuntimeError):
|
||||||
|
d.index(n)
|
||||||
|
|
||||||
|
# Test start and stop arguments behavior matches list.index()
|
||||||
|
elements = 'ABCDEFGHI'
|
||||||
|
nonelement = 'Z'
|
||||||
|
d = deque(elements * 2)
|
||||||
|
s = list(elements * 2)
|
||||||
|
for start in range(-5 - len(s)*2, 5 + len(s) * 2):
|
||||||
|
for stop in range(-5 - len(s)*2, 5 + len(s) * 2):
|
||||||
|
for element in elements + 'Z':
|
||||||
|
try:
|
||||||
|
target = s.index(element, start, stop)
|
||||||
|
except ValueError:
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
d.index(element, start, stop)
|
||||||
|
else:
|
||||||
|
self.assertEqual(d.index(element, start, stop), target)
|
||||||
|
|
||||||
|
def test_insert(self):
|
||||||
|
# Test to make sure insert behaves like lists
|
||||||
|
elements = 'ABCDEFGHI'
|
||||||
|
for i in range(-5 - len(elements)*2, 5 + len(elements) * 2):
|
||||||
|
d = deque('ABCDEFGHI')
|
||||||
|
s = list('ABCDEFGHI')
|
||||||
|
d.insert(i, 'Z')
|
||||||
|
s.insert(i, 'Z')
|
||||||
|
self.assertEqual(list(d), s)
|
||||||
|
|
||||||
def test_setitem(self):
|
def test_setitem(self):
|
||||||
n = 200
|
n = 200
|
||||||
d = deque(range(n))
|
d = deque(range(n))
|
||||||
|
@ -524,6 +572,15 @@ class TestBasic(unittest.TestCase):
|
||||||
self.assertNotEqual(id(d), id(e))
|
self.assertNotEqual(id(d), id(e))
|
||||||
self.assertEqual(list(d), list(e))
|
self.assertEqual(list(d), list(e))
|
||||||
|
|
||||||
|
def test_copy_method(self):
|
||||||
|
mut = [10]
|
||||||
|
d = deque([mut])
|
||||||
|
e = d.copy()
|
||||||
|
self.assertEqual(list(d), list(e))
|
||||||
|
mut[0] = 11
|
||||||
|
self.assertNotEqual(id(d), id(e))
|
||||||
|
self.assertEqual(list(d), list(e))
|
||||||
|
|
||||||
def test_reversed(self):
|
def test_reversed(self):
|
||||||
for s in ('abcd', range(2000)):
|
for s in ('abcd', range(2000)):
|
||||||
self.assertEqual(list(reversed(deque(s))), list(reversed(s)))
|
self.assertEqual(list(reversed(deque(s))), list(reversed(s)))
|
||||||
|
|
|
@ -27,6 +27,10 @@ Library
|
||||||
and socket open until the garbage collector cleans them up. Patch by
|
and socket open until the garbage collector cleans them up. Patch by
|
||||||
Martin Panter.
|
Martin Panter.
|
||||||
|
|
||||||
|
- Issue #23704: collections.deque() objects now support methods for index(),
|
||||||
|
insert(), and copy(). This allows deques to be registered as a
|
||||||
|
MutableSequence and it improves their substitutablity for lists.
|
||||||
|
|
||||||
- Issue #23715: :func:`signal.sigwaitinfo` and :func:`signal.sigtimedwait` are
|
- Issue #23715: :func:`signal.sigwaitinfo` and :func:`signal.sigtimedwait` are
|
||||||
now retried when interrupted by a signal not in the *sigset* parameter, if
|
now retried when interrupted by a signal not in the *sigset* parameter, if
|
||||||
the signal handler does not raise an exception. signal.sigtimedwait()
|
the signal handler does not raise an exception. signal.sigtimedwait()
|
||||||
|
|
|
@ -762,6 +762,91 @@ deque_len(dequeobject *deque)
|
||||||
return Py_SIZE(deque);
|
return Py_SIZE(deque);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
deque_index(dequeobject *deque, PyObject *args)
|
||||||
|
{
|
||||||
|
Py_ssize_t i, start=0, stop=Py_SIZE(deque);
|
||||||
|
PyObject *v, *item;
|
||||||
|
block *b = deque->leftblock;
|
||||||
|
Py_ssize_t index = deque->leftindex;
|
||||||
|
size_t start_state = deque->state;
|
||||||
|
|
||||||
|
if (!PyArg_ParseTuple(args, "O|O&O&:index", &v,
|
||||||
|
_PyEval_SliceIndex, &start,
|
||||||
|
_PyEval_SliceIndex, &stop))
|
||||||
|
return NULL;
|
||||||
|
if (start < 0) {
|
||||||
|
start += Py_SIZE(deque);
|
||||||
|
if (start < 0)
|
||||||
|
start = 0;
|
||||||
|
}
|
||||||
|
if (stop < 0) {
|
||||||
|
stop += Py_SIZE(deque);
|
||||||
|
if (stop < 0)
|
||||||
|
stop = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i=0 ; i<stop ; i++) {
|
||||||
|
if (i >= start) {
|
||||||
|
int cmp;
|
||||||
|
CHECK_NOT_END(b);
|
||||||
|
item = b->data[index];
|
||||||
|
cmp = PyObject_RichCompareBool(item, v, Py_EQ);
|
||||||
|
if (cmp > 0)
|
||||||
|
return PyLong_FromSsize_t(i);
|
||||||
|
else if (cmp < 0)
|
||||||
|
return NULL;
|
||||||
|
if (start_state != deque->state) {
|
||||||
|
PyErr_SetString(PyExc_RuntimeError,
|
||||||
|
"deque mutated during iteration");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
index++;
|
||||||
|
if (index == BLOCKLEN) {
|
||||||
|
b = b->rightlink;
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PyErr_Format(PyExc_ValueError, "%R is not in deque", v);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyDoc_STRVAR(index_doc,
|
||||||
|
"D.index(value, [start, [stop]]) -> integer -- return first index of value.\n"
|
||||||
|
"Raises ValueError if the value is not present.");
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
deque_insert(dequeobject *deque, PyObject *args)
|
||||||
|
{
|
||||||
|
Py_ssize_t index;
|
||||||
|
Py_ssize_t n = Py_SIZE(deque);
|
||||||
|
PyObject *value;
|
||||||
|
PyObject *rv;
|
||||||
|
|
||||||
|
if (!PyArg_ParseTuple(args, "nO:insert", &index, &value))
|
||||||
|
return NULL;
|
||||||
|
if (index >= n)
|
||||||
|
return deque_append(deque, value);
|
||||||
|
if (index <= -n || index == 0)
|
||||||
|
return deque_appendleft(deque, value);
|
||||||
|
if (_deque_rotate(deque, -index))
|
||||||
|
return NULL;
|
||||||
|
if (index < 0)
|
||||||
|
rv = deque_append(deque, value);
|
||||||
|
else
|
||||||
|
rv = deque_appendleft(deque, value);
|
||||||
|
if (rv == NULL)
|
||||||
|
return NULL;
|
||||||
|
Py_DECREF(rv);
|
||||||
|
if (_deque_rotate(deque, index))
|
||||||
|
return NULL;
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyDoc_STRVAR(insert_doc,
|
||||||
|
"D.insert(index, object) -- insert object before index");
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
deque_remove(dequeobject *deque, PyObject *value)
|
deque_remove(dequeobject *deque, PyObject *value)
|
||||||
{
|
{
|
||||||
|
@ -1208,12 +1293,18 @@ static PyMethodDef deque_methods[] = {
|
||||||
METH_NOARGS, clear_doc},
|
METH_NOARGS, clear_doc},
|
||||||
{"__copy__", (PyCFunction)deque_copy,
|
{"__copy__", (PyCFunction)deque_copy,
|
||||||
METH_NOARGS, copy_doc},
|
METH_NOARGS, copy_doc},
|
||||||
|
{"copy", (PyCFunction)deque_copy,
|
||||||
|
METH_NOARGS, copy_doc},
|
||||||
{"count", (PyCFunction)deque_count,
|
{"count", (PyCFunction)deque_count,
|
||||||
METH_O, count_doc},
|
METH_O, count_doc},
|
||||||
{"extend", (PyCFunction)deque_extend,
|
{"extend", (PyCFunction)deque_extend,
|
||||||
METH_O, extend_doc},
|
METH_O, extend_doc},
|
||||||
{"extendleft", (PyCFunction)deque_extendleft,
|
{"extendleft", (PyCFunction)deque_extendleft,
|
||||||
METH_O, extendleft_doc},
|
METH_O, extendleft_doc},
|
||||||
|
{"index", (PyCFunction)deque_index,
|
||||||
|
METH_VARARGS, index_doc},
|
||||||
|
{"insert", (PyCFunction)deque_insert,
|
||||||
|
METH_VARARGS, insert_doc},
|
||||||
{"pop", (PyCFunction)deque_pop,
|
{"pop", (PyCFunction)deque_pop,
|
||||||
METH_NOARGS, pop_doc},
|
METH_NOARGS, pop_doc},
|
||||||
{"popleft", (PyCFunction)deque_popleft,
|
{"popleft", (PyCFunction)deque_popleft,
|
||||||
|
|
Loading…
Reference in New Issue