mirror of
https://github.com/python/cpython.git
synced 2025-12-10 11:00:14 +00:00
Issue 23793: Add deque support for __add__(), __mul__(), and __imul__().
This commit is contained in:
parent
b6c15bcad3
commit
41290a68ba
4 changed files with 204 additions and 10 deletions
|
|
@ -526,6 +526,9 @@ the :keyword:`in` operator, and subscript references such as ``d[-1]``. Indexed
|
||||||
access is O(1) at both ends but slows to O(n) in the middle. For fast random
|
access is O(1) at both ends but slows to O(n) in the middle. For fast random
|
||||||
access, use lists instead.
|
access, use lists instead.
|
||||||
|
|
||||||
|
Starting in version 3.5, deques support ``__add__()``, ``__mul__()``,
|
||||||
|
and ``__imul__()``.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
|
|
||||||
.. doctest::
|
.. doctest::
|
||||||
|
|
|
||||||
|
|
@ -192,6 +192,26 @@ class TestBasic(unittest.TestCase):
|
||||||
d.extend(d)
|
d.extend(d)
|
||||||
self.assertEqual(list(d), list('abcdabcd'))
|
self.assertEqual(list(d), list('abcdabcd'))
|
||||||
|
|
||||||
|
def test_add(self):
|
||||||
|
d = deque()
|
||||||
|
e = deque('abc')
|
||||||
|
f = deque('def')
|
||||||
|
self.assertEqual(d + d, deque())
|
||||||
|
self.assertEqual(e + f, deque('abcdef'))
|
||||||
|
self.assertEqual(e + e, deque('abcabc'))
|
||||||
|
self.assertEqual(e + d, deque('abc'))
|
||||||
|
self.assertEqual(d + e, deque('abc'))
|
||||||
|
self.assertIsNot(d + d, deque())
|
||||||
|
self.assertIsNot(e + d, deque('abc'))
|
||||||
|
self.assertIsNot(d + e, deque('abc'))
|
||||||
|
|
||||||
|
g = deque('abcdef', maxlen=4)
|
||||||
|
h = deque('gh')
|
||||||
|
self.assertEqual(g + h, deque('efgh'))
|
||||||
|
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
deque('abc') + 'def'
|
||||||
|
|
||||||
def test_iadd(self):
|
def test_iadd(self):
|
||||||
d = deque('a')
|
d = deque('a')
|
||||||
d += 'bcd'
|
d += 'bcd'
|
||||||
|
|
@ -279,6 +299,63 @@ class TestBasic(unittest.TestCase):
|
||||||
s.insert(i, 'Z')
|
s.insert(i, 'Z')
|
||||||
self.assertEqual(list(d), s)
|
self.assertEqual(list(d), s)
|
||||||
|
|
||||||
|
def test_imul(self):
|
||||||
|
for n in (-10, -1, 0, 1, 2, 10, 1000):
|
||||||
|
d = deque()
|
||||||
|
d *= n
|
||||||
|
self.assertEqual(d, deque())
|
||||||
|
self.assertIsNone(d.maxlen)
|
||||||
|
|
||||||
|
for n in (-10, -1, 0, 1, 2, 10, 1000):
|
||||||
|
d = deque('a')
|
||||||
|
d *= n
|
||||||
|
self.assertEqual(d, deque('a' * n))
|
||||||
|
self.assertIsNone(d.maxlen)
|
||||||
|
|
||||||
|
for n in (-10, -1, 0, 1, 2, 10, 499, 500, 501, 1000):
|
||||||
|
d = deque('a', 500)
|
||||||
|
d *= n
|
||||||
|
self.assertEqual(d, deque('a' * min(n, 500)))
|
||||||
|
self.assertEqual(d.maxlen, 500)
|
||||||
|
|
||||||
|
for n in (-10, -1, 0, 1, 2, 10, 1000):
|
||||||
|
d = deque('abcdef')
|
||||||
|
d *= n
|
||||||
|
self.assertEqual(d, deque('abcdef' * n))
|
||||||
|
self.assertIsNone(d.maxlen)
|
||||||
|
|
||||||
|
for n in (-10, -1, 0, 1, 2, 10, 499, 500, 501, 1000):
|
||||||
|
d = deque('abcdef', 500)
|
||||||
|
d *= n
|
||||||
|
self.assertEqual(d, deque(('abcdef' * n)[-500:]))
|
||||||
|
self.assertEqual(d.maxlen, 500)
|
||||||
|
|
||||||
|
def test_mul(self):
|
||||||
|
d = deque('abc')
|
||||||
|
self.assertEqual(d * -5, deque())
|
||||||
|
self.assertEqual(d * 0, deque())
|
||||||
|
self.assertEqual(d * 1, deque('abc'))
|
||||||
|
self.assertEqual(d * 2, deque('abcabc'))
|
||||||
|
self.assertEqual(d * 3, deque('abcabcabc'))
|
||||||
|
self.assertIsNot(d * 1, d)
|
||||||
|
|
||||||
|
self.assertEqual(deque() * 0, deque())
|
||||||
|
self.assertEqual(deque() * 1, deque())
|
||||||
|
self.assertEqual(deque() * 5, deque())
|
||||||
|
|
||||||
|
self.assertEqual(-5 * d, deque())
|
||||||
|
self.assertEqual(0 * d, deque())
|
||||||
|
self.assertEqual(1 * d, deque('abc'))
|
||||||
|
self.assertEqual(2 * d, deque('abcabc'))
|
||||||
|
self.assertEqual(3 * d, deque('abcabcabc'))
|
||||||
|
|
||||||
|
d = deque('abc', maxlen=5)
|
||||||
|
self.assertEqual(d * -5, deque())
|
||||||
|
self.assertEqual(d * 0, deque())
|
||||||
|
self.assertEqual(d * 1, deque('abc'))
|
||||||
|
self.assertEqual(d * 2, deque('bcabc'))
|
||||||
|
self.assertEqual(d * 30, deque('bcabc'))
|
||||||
|
|
||||||
def test_setitem(self):
|
def test_setitem(self):
|
||||||
n = 200
|
n = 200
|
||||||
d = deque(range(n))
|
d = deque(range(n))
|
||||||
|
|
|
||||||
|
|
@ -152,6 +152,7 @@ Library
|
||||||
- Issue #23704: collections.deque() objects now support methods for index(),
|
- Issue #23704: collections.deque() objects now support methods for index(),
|
||||||
insert(), and copy(). This allows deques to be registered as a
|
insert(), and copy(). This allows deques to be registered as a
|
||||||
MutableSequence and it improves their substitutablity for lists.
|
MutableSequence and it improves their substitutablity for lists.
|
||||||
|
Deques now also support __add__, __mul__, and __imul__().
|
||||||
|
|
||||||
- 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
|
||||||
|
|
|
||||||
|
|
@ -110,6 +110,12 @@ static PyTypeObject deque_type;
|
||||||
#define CHECK_NOT_END(link)
|
#define CHECK_NOT_END(link)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* To prevent len from overflowing PY_SSIZE_T_MAX, we refuse to
|
||||||
|
allocate new blocks if the current len is nearing overflow.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define MAX_DEQUE_LEN (PY_SSIZE_T_MAX - 3*BLOCKLEN)
|
||||||
|
|
||||||
/* A simple freelisting scheme is used to minimize calls to the memory
|
/* A simple freelisting scheme is used to minimize calls to the memory
|
||||||
allocator. It accommodates common use cases where new blocks are being
|
allocator. It accommodates common use cases where new blocks are being
|
||||||
added at about the same rate as old blocks are being freed.
|
added at about the same rate as old blocks are being freed.
|
||||||
|
|
@ -122,9 +128,7 @@ static block *freeblocks[MAXFREEBLOCKS];
|
||||||
static block *
|
static block *
|
||||||
newblock(Py_ssize_t len) {
|
newblock(Py_ssize_t len) {
|
||||||
block *b;
|
block *b;
|
||||||
/* To prevent len from overflowing PY_SSIZE_T_MAX, we refuse to
|
if (len >= MAX_DEQUE_LEN) {
|
||||||
* allocate new blocks if the current len is nearing overflow. */
|
|
||||||
if (len >= PY_SSIZE_T_MAX - 2*BLOCKLEN) {
|
|
||||||
PyErr_SetString(PyExc_OverflowError,
|
PyErr_SetString(PyExc_OverflowError,
|
||||||
"cannot add more blocks to the deque");
|
"cannot add more blocks to the deque");
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
@ -498,6 +502,115 @@ deque_inplace_concat(dequeobject *deque, PyObject *other)
|
||||||
return (PyObject *)deque;
|
return (PyObject *)deque;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject *deque_copy(PyObject *deque);
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
deque_concat(dequeobject *deque, PyObject *other)
|
||||||
|
{
|
||||||
|
PyObject *new_deque;
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
rv = PyObject_IsInstance(other, (PyObject *)&deque_type);
|
||||||
|
if (rv <= 0) {
|
||||||
|
if (rv == 0) {
|
||||||
|
PyErr_Format(PyExc_TypeError,
|
||||||
|
"can only concatenate deque (not \"%.200s\") to deque",
|
||||||
|
other->ob_type->tp_name);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
new_deque = deque_copy((PyObject *)deque);
|
||||||
|
if (new_deque == NULL)
|
||||||
|
return NULL;
|
||||||
|
return deque_inplace_concat((dequeobject *)new_deque, other);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void deque_clear(dequeobject *deque);
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
deque_repeat(dequeobject *deque, Py_ssize_t n)
|
||||||
|
{
|
||||||
|
dequeobject *new_deque;
|
||||||
|
PyObject *result;
|
||||||
|
|
||||||
|
/* XXX add a special case for when maxlen is defined */
|
||||||
|
if (n < 0)
|
||||||
|
n = 0;
|
||||||
|
else if (n > 0 && Py_SIZE(deque) > MAX_DEQUE_LEN / n)
|
||||||
|
return PyErr_NoMemory();
|
||||||
|
|
||||||
|
new_deque = (dequeobject *)deque_new(&deque_type, (PyObject *)NULL, (PyObject *)NULL);
|
||||||
|
new_deque->maxlen = deque->maxlen;
|
||||||
|
|
||||||
|
for ( ; n ; n--) {
|
||||||
|
result = deque_extend(new_deque, (PyObject *)deque);
|
||||||
|
if (result == NULL) {
|
||||||
|
Py_DECREF(new_deque);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
Py_DECREF(result);
|
||||||
|
}
|
||||||
|
return (PyObject *)new_deque;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
deque_inplace_repeat(dequeobject *deque, Py_ssize_t n)
|
||||||
|
{
|
||||||
|
Py_ssize_t i, size;
|
||||||
|
PyObject *seq;
|
||||||
|
PyObject *rv;
|
||||||
|
|
||||||
|
size = Py_SIZE(deque);
|
||||||
|
if (size == 0 || n == 1) {
|
||||||
|
Py_INCREF(deque);
|
||||||
|
return (PyObject *)deque;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (n <= 0) {
|
||||||
|
deque_clear(deque);
|
||||||
|
Py_INCREF(deque);
|
||||||
|
return (PyObject *)deque;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size > MAX_DEQUE_LEN / n) {
|
||||||
|
return PyErr_NoMemory();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (size == 1) {
|
||||||
|
/* common case, repeating a single element */
|
||||||
|
PyObject *item = deque->leftblock->data[deque->leftindex];
|
||||||
|
|
||||||
|
if (deque->maxlen != -1 && n > deque->maxlen)
|
||||||
|
n = deque->maxlen;
|
||||||
|
|
||||||
|
for (i = 0 ; i < n-1 ; i++) {
|
||||||
|
rv = deque_append(deque, item);
|
||||||
|
if (rv == NULL)
|
||||||
|
return NULL;
|
||||||
|
Py_DECREF(rv);
|
||||||
|
}
|
||||||
|
Py_INCREF(deque);
|
||||||
|
return (PyObject *)deque;
|
||||||
|
}
|
||||||
|
|
||||||
|
seq = PySequence_List((PyObject *)deque);
|
||||||
|
if (seq == NULL)
|
||||||
|
return seq;
|
||||||
|
|
||||||
|
for (i = 0 ; i < n-1 ; i++) {
|
||||||
|
rv = deque_extend(deque, seq);
|
||||||
|
if (rv == NULL) {
|
||||||
|
Py_DECREF(seq);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
Py_DECREF(rv);
|
||||||
|
}
|
||||||
|
Py_INCREF(deque);
|
||||||
|
Py_DECREF(seq);
|
||||||
|
return (PyObject *)deque;
|
||||||
|
}
|
||||||
|
|
||||||
/* The rotate() method is part of the public API and is used internally
|
/* The rotate() method is part of the public API and is used internally
|
||||||
as a primitive for other methods.
|
as a primitive for other methods.
|
||||||
|
|
||||||
|
|
@ -1283,6 +1396,9 @@ deque_get_maxlen(dequeobject *deque)
|
||||||
return PyLong_FromSsize_t(deque->maxlen);
|
return PyLong_FromSsize_t(deque->maxlen);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* deque object ********************************************************/
|
||||||
|
|
||||||
static PyGetSetDef deque_getset[] = {
|
static PyGetSetDef deque_getset[] = {
|
||||||
{"maxlen", (getter)deque_get_maxlen, (setter)NULL,
|
{"maxlen", (getter)deque_get_maxlen, (setter)NULL,
|
||||||
"maximum size of a deque or None if unbounded"},
|
"maximum size of a deque or None if unbounded"},
|
||||||
|
|
@ -1291,15 +1407,15 @@ static PyGetSetDef deque_getset[] = {
|
||||||
|
|
||||||
static PySequenceMethods deque_as_sequence = {
|
static PySequenceMethods deque_as_sequence = {
|
||||||
(lenfunc)deque_len, /* sq_length */
|
(lenfunc)deque_len, /* sq_length */
|
||||||
0, /* sq_concat */
|
(binaryfunc)deque_concat, /* sq_concat */
|
||||||
0, /* sq_repeat */
|
(ssizeargfunc)deque_repeat, /* sq_repeat */
|
||||||
(ssizeargfunc)deque_item, /* sq_item */
|
(ssizeargfunc)deque_item, /* sq_item */
|
||||||
0, /* sq_slice */
|
0, /* sq_slice */
|
||||||
(ssizeobjargproc)deque_ass_item, /* sq_ass_item */
|
(ssizeobjargproc)deque_ass_item, /* sq_ass_item */
|
||||||
0, /* sq_ass_slice */
|
0, /* sq_ass_slice */
|
||||||
(objobjproc)deque_contains, /* sq_contains */
|
(objobjproc)deque_contains, /* sq_contains */
|
||||||
(binaryfunc)deque_inplace_concat, /* sq_inplace_concat */
|
(binaryfunc)deque_inplace_concat, /* sq_inplace_concat */
|
||||||
0, /* sq_inplace_repeat */
|
(ssizeargfunc)deque_inplace_repeat, /* sq_inplace_repeat */
|
||||||
};
|
};
|
||||||
|
|
||||||
static PyNumberMethods deque_as_number = {
|
static PyNumberMethods deque_as_number = {
|
||||||
|
|
@ -1316,9 +1432,6 @@ static PyNumberMethods deque_as_number = {
|
||||||
0, /* nb_invert */
|
0, /* nb_invert */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/* deque object ********************************************************/
|
|
||||||
|
|
||||||
static PyObject *deque_iter(dequeobject *deque);
|
static PyObject *deque_iter(dequeobject *deque);
|
||||||
static PyObject *deque_reviter(dequeobject *deque);
|
static PyObject *deque_reviter(dequeobject *deque);
|
||||||
PyDoc_STRVAR(reversed_doc,
|
PyDoc_STRVAR(reversed_doc,
|
||||||
|
|
@ -1367,7 +1480,7 @@ static PyMethodDef deque_methods[] = {
|
||||||
PyDoc_STRVAR(deque_doc,
|
PyDoc_STRVAR(deque_doc,
|
||||||
"deque([iterable[, maxlen]]) --> deque object\n\
|
"deque([iterable[, maxlen]]) --> deque object\n\
|
||||||
\n\
|
\n\
|
||||||
Build an ordered collection with optimized access from its endpoints.");
|
A list-like sequence optimized for data accesses near its endpoints.");
|
||||||
|
|
||||||
static PyTypeObject deque_type = {
|
static PyTypeObject deque_type = {
|
||||||
PyVarObject_HEAD_INIT(NULL, 0)
|
PyVarObject_HEAD_INIT(NULL, 0)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue