Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 738ec90

Browse files
committed
Improvements to collections.deque():
* Add doctests for the examples in the library reference. * Add two methods, left() and right(), modeled after deques in C++ STL. * Apply the new method to asynchat.py. * Add comparison operators to make deques more substitutable for lists. * Replace the LookupErrors with IndexErrors to more closely match lists.
1 parent fe99927 commit 738ec90

4 files changed

Lines changed: 229 additions & 18 deletions

File tree

Doc/lib/libcollections.tex

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,14 +54,24 @@ \section{\module{collections} ---
5454
reversing the order of elements in the iterable argument.
5555
\end{methoddesc}
5656

57+
\begin{methoddesc}{left}{}
58+
Return leftmost element from the deque.
59+
If no elements are present, raises a \exception{IndexError}.
60+
\end{methoddesc}
61+
5762
\begin{methoddesc}{pop}{}
5863
Remove and return an element from the right side of the deque.
59-
If no elements are present, raises a \exception{LookupError}.
64+
If no elements are present, raises a \exception{IndexError}.
6065
\end{methoddesc}
6166

6267
\begin{methoddesc}{popleft}{}
6368
Remove and return an element from the left side of the deque.
64-
If no elements are present, raises a \exception{LookupError}.
69+
If no elements are present, raises a \exception{IndexError}.
70+
\end{methoddesc}
71+
72+
\begin{methoddesc}{right}{}
73+
Return the rightmost element from the deque.
74+
If no elements are present, raises a \exception{IndexError}.
6575
\end{methoddesc}
6676

6777
\begin{methoddesc}{rotate}{n}
@@ -80,22 +90,27 @@ \section{\module{collections} ---
8090
>>> from collections import deque
8191
>>> d = deque('ghi') # make a new deque with three items
8292
>>> for elem in d: # iterate over the deque's elements
83-
print elem.upper()
84-
85-
93+
... print elem.upper()
8694
G
8795
H
8896
I
97+
8998
>>> d.append('j') # add a new entry to the right side
9099
>>> d.appendleft('f') # add a new entry to the left side
91100
>>> d # show the representation of the deque
92101
deque(['f', 'g', 'h', 'i', 'j'])
102+
93103
>>> d.pop() # return and remove the rightmost item
94104
'j'
95105
>>> d.popleft() # return and remove the leftmost item
96106
'f'
97107
>>> list(d) # list the contents of the deque
98108
['g', 'h', 'i']
109+
110+
>>> d.left() # peek at leftmost item
111+
'g'
112+
>>> d.right() # peek at rightmost item
113+
'i'
99114
>>> list(reversed(d)) # list the contents of a deque in reverse
100115
['i', 'h', 'g']
101116
>>> 'h' in d # search the deque
@@ -109,15 +124,15 @@ \section{\module{collections} ---
109124
>>> d.rotate(-1) # left rotation
110125
>>> d
111126
deque(['g', 'h', 'i', 'j', 'k', 'l'])
127+
112128
>>> deque(reversed(d)) # make a new deque in reverse order
113129
deque(['l', 'k', 'j', 'i', 'h', 'g'])
114130
>>> d.clear() # empty the deque
115131
>>> d.pop() # cannot pop from an empty deque
116-
117132
Traceback (most recent call last):
118133
File "<pyshell#6>", line 1, in -toplevel-
119134
d.pop()
120-
LookupError: pop from an empty deque
135+
IndexError: pop from an empty deque
121136
122137
>>> d.extendleft('abc') # extendleft() reverses the input order
123138
>>> d

Lib/asynchat.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -262,11 +262,7 @@ def is_empty (self):
262262
return self.list == []
263263

264264
def first (self):
265-
it = iter(self.list)
266-
try:
267-
return it.next()
268-
except StopIteration:
269-
raise IndexError
265+
return self.list.left()
270266

271267
def push (self, data):
272268
self.list.append(data)

Lib/test/test_deque.py

Lines changed: 89 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,23 @@ def test_basics(self):
2828
self.assertEqual(right, range(150, 400))
2929
self.assertEqual(list(d), range(50, 150))
3030

31+
def test_comparisons(self):
32+
d = deque('xabc'); d.popleft()
33+
for e in [d, deque('abc'), deque('ab'), deque(), list(d)]:
34+
self.assertEqual(d==e, type(d)==type(e) and list(d)==list(e))
35+
self.assertEqual(d!=e, not(type(d)==type(e) and list(d)==list(e)))
36+
37+
args = map(deque, ('', 'a', 'b', 'ab', 'ba', 'abc', 'xba', 'xabc', 'cba'))
38+
for x in args:
39+
for y in args:
40+
self.assertEqual(x == y, list(x) == list(y), (x,y))
41+
self.assertEqual(x != y, list(x) != list(y), (x,y))
42+
self.assertEqual(x < y, list(x) < list(y), (x,y))
43+
self.assertEqual(x <= y, list(x) <= list(y), (x,y))
44+
self.assertEqual(x > y, list(x) > list(y), (x,y))
45+
self.assertEqual(x >= y, list(x) >= list(y), (x,y))
46+
self.assertEqual(cmp(x,y), cmp(list(x),list(y)), (x,y))
47+
3148
def test_extend(self):
3249
d = deque('a')
3350
self.assertRaises(TypeError, d.extend, 1)
@@ -40,6 +57,14 @@ def test_extendleft(self):
4057
d.extendleft('bcd')
4158
self.assertEqual(list(d), list(reversed('abcd')))
4259

60+
def test_leftright(self):
61+
d = deque('superman')
62+
self.assertEqual(d.left(), 's')
63+
self.assertEqual(d.right(), 'n')
64+
d = deque()
65+
self.assertRaises(IndexError, d.left)
66+
self.assertRaises(IndexError, d.right)
67+
4368
def test_rotate(self):
4469
s = tuple('abcde')
4570
n = len(s)
@@ -93,7 +118,7 @@ def test_len(self):
93118
self.assertEqual(len(d), 1)
94119
d.pop()
95120
self.assertEqual(len(d), 0)
96-
self.assertRaises(LookupError, d.pop)
121+
self.assertRaises(IndexError, d.pop)
97122
self.assertEqual(len(d), 0)
98123
d.append('c')
99124
self.assertEqual(len(d), 1)
@@ -104,8 +129,8 @@ def test_len(self):
104129

105130
def test_underflow(self):
106131
d = deque()
107-
self.assertRaises(LookupError, d.pop)
108-
self.assertRaises(LookupError, d.popleft)
132+
self.assertRaises(IndexError, d.pop)
133+
self.assertRaises(IndexError, d.popleft)
109134

110135
def test_clear(self):
111136
d = deque(xrange(100))
@@ -374,6 +399,63 @@ def test_copy_pickle(self):
374399

375400
#==============================================================================
376401

402+
libreftest = """
403+
Example from the Library Reference: Doc/lib/libcollections.tex
404+
405+
>>> from collections import deque
406+
>>> d = deque('ghi') # make a new deque with three items
407+
>>> for elem in d: # iterate over the deque's elements
408+
... print elem.upper()
409+
G
410+
H
411+
I
412+
>>> d.append('j') # add a new entry to the right side
413+
>>> d.appendleft('f') # add a new entry to the left side
414+
>>> d # show the representation of the deque
415+
deque(['f', 'g', 'h', 'i', 'j'])
416+
>>> d.pop() # return and remove the rightmost item
417+
'j'
418+
>>> d.popleft() # return and remove the leftmost item
419+
'f'
420+
>>> list(d) # list the contents of the deque
421+
['g', 'h', 'i']
422+
>>> d.left() # peek at leftmost item
423+
'g'
424+
>>> d.right() # peek at rightmost item
425+
'i'
426+
>>> list(reversed(d)) # list the contents of a deque in reverse
427+
['i', 'h', 'g']
428+
>>> 'h' in d # search the deque
429+
True
430+
>>> d.extend('jkl') # add multiple elements at once
431+
>>> d
432+
deque(['g', 'h', 'i', 'j', 'k', 'l'])
433+
>>> d.rotate(1) # right rotation
434+
>>> d
435+
deque(['l', 'g', 'h', 'i', 'j', 'k'])
436+
>>> d.rotate(-1) # left rotation
437+
>>> d
438+
deque(['g', 'h', 'i', 'j', 'k', 'l'])
439+
>>> deque(reversed(d)) # make a new deque in reverse order
440+
deque(['l', 'k', 'j', 'i', 'h', 'g'])
441+
>>> d.clear() # empty the deque
442+
>>> d.pop() # cannot pop from an empty deque
443+
Traceback (most recent call last):
444+
File "<pyshell#6>", line 1, in -toplevel-
445+
d.pop()
446+
IndexError: pop from an empty deque
447+
448+
>>> d.extendleft('abc') # extendleft() reverses the input order
449+
>>> d
450+
deque(['c', 'b', 'a'])
451+
452+
"""
453+
454+
455+
#==============================================================================
456+
457+
__test__ = {'libreftest' : libreftest}
458+
377459
def test_main(verbose=None):
378460
import sys
379461
from test import test_sets
@@ -394,6 +476,10 @@ def test_main(verbose=None):
394476
gc.collect()
395477
counts[i] = sys.gettotalrefcount()
396478
print counts
479+
480+
# doctests
481+
from test import test_deque
482+
test_support.run_doctest(test_deque, verbose)
397483

398484
if __name__ == "__main__":
399485
test_main(verbose=True)

Modules/collectionsmodule.c

Lines changed: 117 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ typedef struct {
3434
int len;
3535
} dequeobject;
3636

37+
PyTypeObject deque_type;
38+
3739
static PyObject *
3840
deque_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
3941
{
@@ -109,7 +111,7 @@ deque_pop(dequeobject *deque, PyObject *unused)
109111
block *prevblock;
110112

111113
if (deque->len == 0) {
112-
PyErr_SetString(PyExc_LookupError, "pop from an empty deque");
114+
PyErr_SetString(PyExc_IndexError, "pop from an empty deque");
113115
return NULL;
114116
}
115117
item = deque->rightblock->data[deque->rightindex];
@@ -144,7 +146,7 @@ deque_popleft(dequeobject *deque, PyObject *unused)
144146
block *prevblock;
145147

146148
if (deque->len == 0) {
147-
PyErr_SetString(PyExc_LookupError, "pop from an empty deque");
149+
PyErr_SetString(PyExc_IndexError, "pop from an empty deque");
148150
return NULL;
149151
}
150152
item = deque->leftblock->data[deque->leftindex];
@@ -174,6 +176,38 @@ deque_popleft(dequeobject *deque, PyObject *unused)
174176

175177
PyDoc_STRVAR(popleft_doc, "Remove and return the leftmost element.");
176178

179+
static PyObject *
180+
deque_right(dequeobject *deque, PyObject *unused)
181+
{
182+
PyObject *item;
183+
184+
if (deque->len == 0) {
185+
PyErr_SetString(PyExc_IndexError, "deque is empty");
186+
return NULL;
187+
}
188+
item = deque->rightblock->data[deque->rightindex];
189+
Py_INCREF(item);
190+
return item;
191+
}
192+
193+
PyDoc_STRVAR(right_doc, "Return the rightmost element.");
194+
195+
static PyObject *
196+
deque_left(dequeobject *deque, PyObject *unused)
197+
{
198+
PyObject *item;
199+
200+
if (deque->len == 0) {
201+
PyErr_SetString(PyExc_IndexError, "deque is empty");
202+
return NULL;
203+
}
204+
item = deque->leftblock->data[deque->leftindex];
205+
Py_INCREF(item);
206+
return item;
207+
}
208+
209+
PyDoc_STRVAR(left_doc, "Return the leftmost element.");
210+
177211
static PyObject *
178212
deque_extend(dequeobject *deque, PyObject *iterable)
179213
{
@@ -467,6 +501,82 @@ deque_tp_print(PyObject *deque, FILE *fp, int flags)
467501
return 0;
468502
}
469503

504+
static PyObject *
505+
deque_richcompare(PyObject *v, PyObject *w, int op)
506+
{
507+
PyObject *it1=NULL, *it2=NULL, *x, *y;
508+
int i, b, vs, ws, minlen, cmp=-1;
509+
510+
if (v->ob_type != &deque_type || w->ob_type != &deque_type) {
511+
Py_INCREF(Py_NotImplemented);
512+
return Py_NotImplemented;
513+
}
514+
515+
/* Shortcuts */
516+
vs = ((dequeobject *)v)->len;
517+
ws = ((dequeobject *)w)->len;
518+
if (op == Py_EQ) {
519+
if (v == w)
520+
Py_RETURN_TRUE;
521+
if (vs != ws)
522+
Py_RETURN_FALSE;
523+
}
524+
if (op == Py_NE) {
525+
if (v == w)
526+
Py_RETURN_FALSE;
527+
if (vs != ws)
528+
Py_RETURN_TRUE;
529+
}
530+
531+
/* Search for the first index where items are different */
532+
it1 = PyObject_GetIter(v);
533+
if (it1 == NULL)
534+
goto done;
535+
it2 = PyObject_GetIter(w);
536+
if (it2 == NULL)
537+
goto done;
538+
minlen = (vs < ws) ? vs : ws;
539+
for (i=0 ; i < minlen ; i++) {
540+
x = PyIter_Next(it1);
541+
if (x == NULL)
542+
goto done;
543+
y = PyIter_Next(it2);
544+
if (y == NULL) {
545+
Py_DECREF(x);
546+
goto done;
547+
}
548+
b = PyObject_RichCompareBool(x, y, Py_EQ);
549+
if (b == 0) {
550+
cmp = PyObject_RichCompareBool(x, y, op);
551+
Py_DECREF(x);
552+
Py_DECREF(y);
553+
goto done;
554+
}
555+
Py_DECREF(x);
556+
Py_DECREF(y);
557+
if (b == -1)
558+
goto done;
559+
}
560+
/* Elements are equal through minlen. The longest input is the greatest */
561+
switch (op) {
562+
case Py_LT: cmp = vs < ws; break;
563+
case Py_LE: cmp = vs <= ws; break;
564+
case Py_EQ: cmp = vs == ws; break;
565+
case Py_NE: cmp = vs != ws; break;
566+
case Py_GT: cmp = vs > ws; break;
567+
case Py_GE: cmp = vs >= ws; break;
568+
}
569+
570+
done:
571+
Py_XDECREF(it1);
572+
Py_XDECREF(it2);
573+
if (cmp == 1)
574+
Py_RETURN_TRUE;
575+
if (cmp == 0)
576+
Py_RETURN_FALSE;
577+
return NULL;
578+
}
579+
470580
static int
471581
deque_init(dequeobject *deque, PyObject *args, PyObject *kwds)
472582
{
@@ -509,6 +619,8 @@ static PyMethodDef deque_methods[] = {
509619
METH_O, extend_doc},
510620
{"extendleft", (PyCFunction)deque_extendleft,
511621
METH_O, extendleft_doc},
622+
{"left", (PyCFunction)deque_left,
623+
METH_NOARGS, left_doc},
512624
{"pop", (PyCFunction)deque_pop,
513625
METH_NOARGS, pop_doc},
514626
{"popleft", (PyCFunction)deque_popleft,
@@ -517,6 +629,8 @@ static PyMethodDef deque_methods[] = {
517629
METH_NOARGS, reduce_doc},
518630
{"__reversed__", (PyCFunction)deque_reviter,
519631
METH_NOARGS, reversed_doc},
632+
{"right", (PyCFunction)deque_right,
633+
METH_NOARGS, right_doc},
520634
{"rotate", (PyCFunction)deque_rotate,
521635
METH_VARARGS, rotate_doc},
522636
{NULL, NULL} /* sentinel */
@@ -553,7 +667,7 @@ PyTypeObject deque_type = {
553667
deque_doc, /* tp_doc */
554668
(traverseproc)set_traverse, /* tp_traverse */
555669
(inquiry)deque_clear, /* tp_clear */
556-
0, /* tp_richcompare */
670+
(richcmpfunc)deque_richcompare, /* tp_richcompare */
557671
0, /* tp_weaklistoffset*/
558672
(getiterfunc)deque_iter, /* tp_iter */
559673
0, /* tp_iternext */

0 commit comments

Comments
 (0)