Commit 0a4977c2 authored by Raymond Hettinger's avatar Raymond Hettinger

Replace left(), right(), and __reversed__() with the more general purpose

__getitem__() and __setitem__().

Simplifies the API, reduces the code size, adds flexibility, and makes
deques work with bisect.bisect(), random.shuffle(), and random.sample().
parent 786ea6bc
...@@ -54,11 +54,6 @@ Deque objects support the following methods: ...@@ -54,11 +54,6 @@ Deque objects support the following methods:
reversing the order of elements in the iterable argument. reversing the order of elements in the iterable argument.
\end{methoddesc} \end{methoddesc}
\begin{methoddesc}{left}{}
Return leftmost element from the deque.
If no elements are present, raises a \exception{IndexError}.
\end{methoddesc}
\begin{methoddesc}{pop}{} \begin{methoddesc}{pop}{}
Remove and return an element from the right side of the deque. Remove and return an element from the right side of the deque.
If no elements are present, raises a \exception{IndexError}. If no elements are present, raises a \exception{IndexError}.
...@@ -69,11 +64,6 @@ Deque objects support the following methods: ...@@ -69,11 +64,6 @@ Deque objects support the following methods:
If no elements are present, raises a \exception{IndexError}. If no elements are present, raises a \exception{IndexError}.
\end{methoddesc} \end{methoddesc}
\begin{methoddesc}{right}{}
Return the rightmost element from the deque.
If no elements are present, raises a \exception{IndexError}.
\end{methoddesc}
\begin{methoddesc}{rotate}{n} \begin{methoddesc}{rotate}{n}
Rotate the deque \var{n} steps to the right. If \var{n} is Rotate the deque \var{n} steps to the right. If \var{n} is
negative, rotate to the left. Rotating one step to the right negative, rotate to the left. Rotating one step to the right
...@@ -81,8 +71,9 @@ Deque objects support the following methods: ...@@ -81,8 +71,9 @@ Deque objects support the following methods:
\end{methoddesc} \end{methoddesc}
In addition to the above, deques support iteration, pickling, \samp{len(d)}, In addition to the above, deques support iteration, pickling, \samp{len(d)},
\samp{reversed(d)}, \samp{copy.copy(d)}, \samp{copy.deepcopy(d)}, and \samp{reversed(d)}, \samp{copy.copy(d)}, \samp{copy.deepcopy(d)},
membership testing with the \keyword{in} operator. membership testing with the \keyword{in} operator, and subscript references
such as \samp{d[-1]}.
Example: Example:
...@@ -106,11 +97,11 @@ deque(['f', 'g', 'h', 'i', 'j']) ...@@ -106,11 +97,11 @@ deque(['f', 'g', 'h', 'i', 'j'])
'f' 'f'
>>> list(d) # list the contents of the deque >>> list(d) # list the contents of the deque
['g', 'h', 'i'] ['g', 'h', 'i']
>>> d[0] # peek at leftmost item
>>> d.left() # peek at leftmost item
'g' 'g'
>>> d.right() # peek at rightmost item >>> d[-1] # peek at rightmost item
'i' 'i'
>>> list(reversed(d)) # list the contents of a deque in reverse >>> list(reversed(d)) # list the contents of a deque in reverse
['i', 'h', 'g'] ['i', 'h', 'g']
>>> 'h' in d # search the deque >>> 'h' in d # search the deque
......
...@@ -262,7 +262,7 @@ class fifo: ...@@ -262,7 +262,7 @@ class fifo:
return self.list == [] return self.list == []
def first (self): def first (self):
return self.list.left() return self.list[0]
def push (self, data): def push (self, data):
self.list.append(data) self.list.append(data)
......
...@@ -4,6 +4,7 @@ from test import test_support ...@@ -4,6 +4,7 @@ from test import test_support
import copy import copy
import cPickle as pickle import cPickle as pickle
from cStringIO import StringIO from cStringIO import StringIO
import random
BIG = 100000 BIG = 100000
...@@ -57,13 +58,37 @@ class TestBasic(unittest.TestCase): ...@@ -57,13 +58,37 @@ class TestBasic(unittest.TestCase):
d.extendleft('bcd') d.extendleft('bcd')
self.assertEqual(list(d), list(reversed('abcd'))) self.assertEqual(list(d), list(reversed('abcd')))
def test_leftright(self): def test_getitem(self):
n = 200
d = deque(xrange(n))
l = range(n)
for i in xrange(n):
d.popleft()
l.pop(0)
if random.random() < 0.5:
d.append(i)
l.append(i)
for j in xrange(1-len(l), len(l)):
assert d[j] == l[j]
d = deque('superman') d = deque('superman')
self.assertEqual(d.left(), 's') self.assertEqual(d[0], 's')
self.assertEqual(d.right(), 'n') self.assertEqual(d[-1], 'n')
d = deque() d = deque()
self.assertRaises(IndexError, d.left) self.assertRaises(IndexError, d.__getitem__, 0)
self.assertRaises(IndexError, d.right) self.assertRaises(IndexError, d.__getitem__, -1)
def test_setitem(self):
n = 200
d = deque(xrange(n))
for i in xrange(n):
d[i] = 10 * i
self.assertEqual(list(d), [10*i for i in xrange(n)])
l = list(d)
for i in xrange(1-n, 0, -1):
d[i] = 7*i
l[i] = 7*i
self.assertEqual(list(d), l)
def test_rotate(self): def test_rotate(self):
s = tuple('abcde') s = tuple('abcde')
...@@ -419,9 +444,9 @@ deque(['f', 'g', 'h', 'i', 'j']) ...@@ -419,9 +444,9 @@ deque(['f', 'g', 'h', 'i', 'j'])
'f' 'f'
>>> list(d) # list the contents of the deque >>> list(d) # list the contents of the deque
['g', 'h', 'i'] ['g', 'h', 'i']
>>> d.left() # peek at leftmost item >>> d[0] # peek at leftmost item
'g' 'g'
>>> d.right() # peek at rightmost item >>> d[-1] # peek at rightmost item
'i' 'i'
>>> list(reversed(d)) # list the contents of a deque in reverse >>> list(reversed(d)) # list the contents of a deque in reverse
['i', 'h', 'g'] ['i', 'h', 'g']
......
...@@ -176,38 +176,6 @@ deque_popleft(dequeobject *deque, PyObject *unused) ...@@ -176,38 +176,6 @@ deque_popleft(dequeobject *deque, PyObject *unused)
PyDoc_STRVAR(popleft_doc, "Remove and return the leftmost element."); PyDoc_STRVAR(popleft_doc, "Remove and return the leftmost element.");
static PyObject *
deque_right(dequeobject *deque, PyObject *unused)
{
PyObject *item;
if (deque->len == 0) {
PyErr_SetString(PyExc_IndexError, "deque is empty");
return NULL;
}
item = deque->rightblock->data[deque->rightindex];
Py_INCREF(item);
return item;
}
PyDoc_STRVAR(right_doc, "Return the rightmost element.");
static PyObject *
deque_left(dequeobject *deque, PyObject *unused)
{
PyObject *item;
if (deque->len == 0) {
PyErr_SetString(PyExc_IndexError, "deque is empty");
return NULL;
}
item = deque->leftblock->data[deque->leftindex];
Py_INCREF(item);
return item;
}
PyDoc_STRVAR(left_doc, "Return the leftmost element.");
static PyObject * static PyObject *
deque_extend(dequeobject *deque, PyObject *iterable) deque_extend(dequeobject *deque, PyObject *iterable)
{ {
...@@ -345,6 +313,69 @@ deque_clear(dequeobject *deque) ...@@ -345,6 +313,69 @@ deque_clear(dequeobject *deque)
return 0; return 0;
} }
static PyObject *
deque_item(dequeobject *deque, int i)
{
block *b;
PyObject *item;
int n;
if (i < 0 || i >= deque->len) {
PyErr_SetString(PyExc_IndexError,
"deque index out of range");
return NULL;
}
i += deque->leftindex;
n = i / BLOCKLEN;
i %= BLOCKLEN;
if (i < (deque->len >> 1)) {
b = deque->leftblock;
while (n--)
b = b->rightlink;
} else {
n = (deque->leftindex + deque->len - 1) / BLOCKLEN - n;
b = deque->rightblock;
while (n--)
b = b->leftlink;
}
item = b->data[i];
Py_INCREF(item);
return item;
}
static int
deque_ass_item(dequeobject *deque, int i, PyObject *v)
{
PyObject *old_value;
block *b;
int n;
if (i < 0 || i >= deque->len) {
PyErr_SetString(PyExc_IndexError,
"deque index out of range");
return -1;
}
i += deque->leftindex;
n = i / BLOCKLEN;
i %= BLOCKLEN;
if (i < (deque->len >> 1)) {
b = deque->leftblock;
while (n--)
b = b->rightlink;
} else {
n = (deque->leftindex + deque->len - 1) / BLOCKLEN - n;
b = deque->rightblock;
while (n--)
b = b->leftlink;
}
Py_INCREF(v);
old_value = b->data[i];
b->data[i] = v;
Py_DECREF(old_value);
return 0;
}
static PyObject * static PyObject *
deque_clearmethod(dequeobject *deque) deque_clearmethod(dequeobject *deque)
{ {
...@@ -371,7 +402,7 @@ deque_dealloc(dequeobject *deque) ...@@ -371,7 +402,7 @@ deque_dealloc(dequeobject *deque)
} }
static int static int
set_traverse(dequeobject *deque, visitproc visit, void *arg) deque_traverse(dequeobject *deque, visitproc visit, void *arg)
{ {
block * b = deque->leftblock; block * b = deque->leftblock;
int index = deque->leftindex; int index = deque->leftindex;
...@@ -597,14 +628,15 @@ deque_init(dequeobject *deque, PyObject *args, PyObject *kwds) ...@@ -597,14 +628,15 @@ deque_init(dequeobject *deque, PyObject *args, PyObject *kwds)
static PySequenceMethods deque_as_sequence = { static PySequenceMethods deque_as_sequence = {
(inquiry)deque_len, /* sq_length */ (inquiry)deque_len, /* sq_length */
0, /* sq_concat */ 0, /* sq_concat */
0, /* sq_repeat */
(intargfunc)deque_item, /* sq_item */
0, /* sq_slice */
(intobjargproc)deque_ass_item, /* sq_ass_item */
}; };
/* deque object ********************************************************/ /* deque object ********************************************************/
static PyObject *deque_iter(dequeobject *deque); static PyObject *deque_iter(dequeobject *deque);
static PyObject *deque_reviter(dequeobject *deque);
PyDoc_STRVAR(reversed_doc,
"D.__reversed__() -- return a reverse iterator over the deque");
static PyMethodDef deque_methods[] = { static PyMethodDef deque_methods[] = {
{"append", (PyCFunction)deque_append, {"append", (PyCFunction)deque_append,
...@@ -619,18 +651,12 @@ static PyMethodDef deque_methods[] = { ...@@ -619,18 +651,12 @@ static PyMethodDef deque_methods[] = {
METH_O, extend_doc}, METH_O, extend_doc},
{"extendleft", (PyCFunction)deque_extendleft, {"extendleft", (PyCFunction)deque_extendleft,
METH_O, extendleft_doc}, METH_O, extendleft_doc},
{"left", (PyCFunction)deque_left,
METH_NOARGS, left_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,
METH_NOARGS, popleft_doc}, METH_NOARGS, popleft_doc},
{"__reduce__", (PyCFunction)deque_reduce, {"__reduce__", (PyCFunction)deque_reduce,
METH_NOARGS, reduce_doc}, METH_NOARGS, reduce_doc},
{"__reversed__", (PyCFunction)deque_reviter,
METH_NOARGS, reversed_doc},
{"right", (PyCFunction)deque_right,
METH_NOARGS, right_doc},
{"rotate", (PyCFunction)deque_rotate, {"rotate", (PyCFunction)deque_rotate,
METH_VARARGS, rotate_doc}, METH_VARARGS, rotate_doc},
{NULL, NULL} /* sentinel */ {NULL, NULL} /* sentinel */
...@@ -665,7 +691,7 @@ static PyTypeObject deque_type = { ...@@ -665,7 +691,7 @@ static PyTypeObject deque_type = {
0, /* tp_as_buffer */ 0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /* tp_flags */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /* tp_flags */
deque_doc, /* tp_doc */ deque_doc, /* tp_doc */
(traverseproc)set_traverse, /* tp_traverse */ (traverseproc)deque_traverse, /* tp_traverse */
(inquiry)deque_clear, /* tp_clear */ (inquiry)deque_clear, /* tp_clear */
(richcmpfunc)deque_richcompare, /* tp_richcompare */ (richcmpfunc)deque_richcompare, /* tp_richcompare */
0, /* tp_weaklistoffset*/ 0, /* tp_weaklistoffset*/
...@@ -777,83 +803,6 @@ PyTypeObject dequeiter_type = { ...@@ -777,83 +803,6 @@ PyTypeObject dequeiter_type = {
0, 0,
}; };
/*********************** Deque Reverse Iterator **************************/
PyTypeObject dequereviter_type;
static PyObject *
deque_reviter(dequeobject *deque)
{
dequeiterobject *it;
it = PyObject_New(dequeiterobject, &dequereviter_type);
if (it == NULL)
return NULL;
it->b = deque->rightblock;
it->index = deque->rightindex;
Py_INCREF(deque);
it->deque = deque;
it->len = deque->len;
return (PyObject *)it;
}
static PyObject *
dequereviter_next(dequeiterobject *it)
{
PyObject *item;
if (it->b == it->deque->leftblock && it->index < it->deque->leftindex)
return NULL;
if (it->len != it->deque->len) {
it->len = -1; /* Make this state sticky */
PyErr_SetString(PyExc_RuntimeError,
"deque changed size during iteration");
return NULL;
}
item = it->b->data[it->index];
it->index--;
if (it->index == -1 && it->b->leftlink != NULL) {
it->b = it->b->leftlink;
it->index = BLOCKLEN - 1;
}
Py_INCREF(item);
return item;
}
PyTypeObject dequereviter_type = {
PyObject_HEAD_INIT(NULL)
0, /* ob_size */
"deque_reverse_iterator", /* tp_name */
sizeof(dequeiterobject), /* tp_basicsize */
0, /* tp_itemsize */
/* methods */
(destructor)dequeiter_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
PyObject_GenericGetAttr, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
0, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
PyObject_SelfIter, /* tp_iter */
(iternextfunc)dequereviter_next, /* tp_iternext */
0,
};
/* module level code ********************************************************/ /* module level code ********************************************************/
PyDoc_STRVAR(module_doc, PyDoc_STRVAR(module_doc,
...@@ -875,8 +824,5 @@ initcollections(void) ...@@ -875,8 +824,5 @@ initcollections(void)
if (PyType_Ready(&dequeiter_type) < 0) if (PyType_Ready(&dequeiter_type) < 0)
return; return;
if (PyType_Ready(&dequereviter_type) < 0)
return;
return; return;
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment