mirror of https://github.com/python/cpython
Add __asdict__() to NamedTuple and refine the docs.
Add maxlen support to deque() and fixup docs. Partially fix __reduce__(). The None as a third arg was no longer supported. Still needs work on __reduce__() to handle recursive inputs.
This commit is contained in:
parent
c9b7163da5
commit
a7fc4b13e0
|
@ -34,7 +34,7 @@ ordered dictionaries.
|
||||||
----------------------
|
----------------------
|
||||||
|
|
||||||
|
|
||||||
.. class:: deque([iterable])
|
.. class:: deque([iterable[, maxlen]])
|
||||||
|
|
||||||
Returns a new deque object initialized left-to-right (using :meth:`append`) with
|
Returns a new deque object initialized left-to-right (using :meth:`append`) with
|
||||||
data from *iterable*. If *iterable* is not specified, the new deque is empty.
|
data from *iterable*. If *iterable* is not specified, the new deque is empty.
|
||||||
|
@ -51,6 +51,17 @@ ordered dictionaries.
|
||||||
|
|
||||||
.. versionadded:: 2.4
|
.. versionadded:: 2.4
|
||||||
|
|
||||||
|
If *maxlen* is not specified or is *-1*, deques may grow to an
|
||||||
|
arbitrary length. Otherwise, the deque is bounded to the specified maximum
|
||||||
|
length. Once a bounded length deque is full, when new items are added, a
|
||||||
|
corresponding number of items are discarded from the opposite end. Bounded
|
||||||
|
length deques provide functionality similar to the ``tail`` filter in
|
||||||
|
Unix. They are also useful for tracking transactions and other pools of data
|
||||||
|
where only the most recent activity is of interest.
|
||||||
|
|
||||||
|
.. versionchanged:: 2.6
|
||||||
|
Added *maxlen*
|
||||||
|
|
||||||
Deque objects support the following methods:
|
Deque objects support the following methods:
|
||||||
|
|
||||||
|
|
||||||
|
@ -168,8 +179,8 @@ Example::
|
||||||
|
|
||||||
.. _deque-recipes:
|
.. _deque-recipes:
|
||||||
|
|
||||||
Recipes
|
:class:`deque` Recipes
|
||||||
^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
This section shows various approaches to working with deques.
|
This section shows various approaches to working with deques.
|
||||||
|
|
||||||
|
@ -186,42 +197,14 @@ To implement :class:`deque` slicing, use a similar approach applying
|
||||||
:meth:`rotate` to bring a target element to the left side of the deque. Remove
|
:meth:`rotate` to bring a target element to the left side of the deque. Remove
|
||||||
old entries with :meth:`popleft`, add new entries with :meth:`extend`, and then
|
old entries with :meth:`popleft`, add new entries with :meth:`extend`, and then
|
||||||
reverse the rotation.
|
reverse the rotation.
|
||||||
|
|
||||||
With minor variations on that approach, it is easy to implement Forth style
|
With minor variations on that approach, it is easy to implement Forth style
|
||||||
stack manipulations such as ``dup``, ``drop``, ``swap``, ``over``, ``pick``,
|
stack manipulations such as ``dup``, ``drop``, ``swap``, ``over``, ``pick``,
|
||||||
``rot``, and ``roll``.
|
``rot``, and ``roll``.
|
||||||
|
|
||||||
A roundrobin task server can be built from a :class:`deque` using
|
|
||||||
:meth:`popleft` to select the current task and :meth:`append` to add it back to
|
|
||||||
the tasklist if the input stream is not exhausted::
|
|
||||||
|
|
||||||
>>> def roundrobin(*iterables):
|
|
||||||
... pending = deque(iter(i) for i in iterables)
|
|
||||||
... while pending:
|
|
||||||
... task = pending.popleft()
|
|
||||||
... try:
|
|
||||||
... yield task.next()
|
|
||||||
... except StopIteration:
|
|
||||||
... continue
|
|
||||||
... pending.append(task)
|
|
||||||
...
|
|
||||||
>>> for value in roundrobin('abc', 'd', 'efgh'):
|
|
||||||
... print value
|
|
||||||
|
|
||||||
a
|
|
||||||
d
|
|
||||||
e
|
|
||||||
b
|
|
||||||
f
|
|
||||||
c
|
|
||||||
g
|
|
||||||
h
|
|
||||||
|
|
||||||
|
|
||||||
Multi-pass data reduction algorithms can be succinctly expressed and efficiently
|
Multi-pass data reduction algorithms can be succinctly expressed and efficiently
|
||||||
coded by extracting elements with multiple calls to :meth:`popleft`, applying
|
coded by extracting elements with multiple calls to :meth:`popleft`, applying
|
||||||
the reduction function, and calling :meth:`append` to add the result back to the
|
a reduction function, and calling :meth:`append` to add the result back to the
|
||||||
queue.
|
deque.
|
||||||
|
|
||||||
For example, building a balanced binary tree of nested lists entails reducing
|
For example, building a balanced binary tree of nested lists entails reducing
|
||||||
two adjacent nodes into one by grouping them in a list::
|
two adjacent nodes into one by grouping them in a list::
|
||||||
|
@ -236,7 +219,12 @@ two adjacent nodes into one by grouping them in a list::
|
||||||
>>> print maketree('abcdefgh')
|
>>> print maketree('abcdefgh')
|
||||||
[[[['a', 'b'], ['c', 'd']], [['e', 'f'], ['g', 'h']]]]
|
[[[['a', 'b'], ['c', 'd']], [['e', 'f'], ['g', 'h']]]]
|
||||||
|
|
||||||
|
Bounded length deques provide functionality similar to the ``tail`` filter
|
||||||
|
in Unix::
|
||||||
|
|
||||||
|
def tail(filename, n=10):
|
||||||
|
'Return the last n lines of a file'
|
||||||
|
return deque(open(filename), n)
|
||||||
|
|
||||||
.. _defaultdict-objects:
|
.. _defaultdict-objects:
|
||||||
|
|
||||||
|
@ -376,7 +364,8 @@ they add the ability to access fields by name instead of position index.
|
||||||
method which lists the tuple contents in a ``name=value`` format.
|
method which lists the tuple contents in a ``name=value`` format.
|
||||||
|
|
||||||
The *fieldnames* are specified in a single string with each fieldname separated by
|
The *fieldnames* are specified in a single string with each fieldname separated by
|
||||||
a space and/or comma. Any valid Python identifier may be used for a fieldname.
|
a space and/or comma. Any valid Python identifier may be used for a fieldname
|
||||||
|
except for names starting and ending with double underscores.
|
||||||
|
|
||||||
If *verbose* is true, will print the class definition.
|
If *verbose* is true, will print the class definition.
|
||||||
|
|
||||||
|
@ -387,7 +376,7 @@ they add the ability to access fields by name instead of position index.
|
||||||
|
|
||||||
Example::
|
Example::
|
||||||
|
|
||||||
>>> Point = NamedTuple('Point', 'x y', True)
|
>>> Point = NamedTuple('Point', 'x y', verbose=True)
|
||||||
class Point(tuple):
|
class Point(tuple):
|
||||||
'Point(x, y)'
|
'Point(x, y)'
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
|
@ -396,6 +385,9 @@ Example::
|
||||||
return tuple.__new__(cls, (x, y))
|
return tuple.__new__(cls, (x, y))
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return 'Point(x=%r, y=%r)' % self
|
return 'Point(x=%r, y=%r)' % self
|
||||||
|
def __asdict__(self):
|
||||||
|
'Return a new dict mapping field names to their values'
|
||||||
|
return dict(zip(('x', 'y'), self))
|
||||||
def __replace__(self, field, value):
|
def __replace__(self, field, value):
|
||||||
'Return a new Point object replacing one field with a new value'
|
'Return a new Point object replacing one field with a new value'
|
||||||
return Point(**dict(zip(('x', 'y'), self) + [(field, value)]))
|
return Point(**dict(zip(('x', 'y'), self) + [(field, value)]))
|
||||||
|
@ -429,10 +421,25 @@ the values::
|
||||||
>>> Point(*t) # the star-operator unpacks any iterable object
|
>>> Point(*t) # the star-operator unpacks any iterable object
|
||||||
Point(x=11, y=22)
|
Point(x=11, y=22)
|
||||||
|
|
||||||
In addition to the methods inherited from tuples, named tuples support
|
When casting a dictionary to a *NamedTuple*, use the double-star-operator::
|
||||||
an additonal method and an informational read-only attribute.
|
|
||||||
|
|
||||||
.. method:: somenamedtuple.replace(field, value)
|
>>> d = {'x': 11, 'y': 22}
|
||||||
|
>>> Point(**d)
|
||||||
|
Point(x=11, y=22)
|
||||||
|
|
||||||
|
In addition to the methods inherited from tuples, named tuples support
|
||||||
|
additonal methods and a read-only attribute.
|
||||||
|
|
||||||
|
.. method:: somenamedtuple.__asdict__()
|
||||||
|
|
||||||
|
Return a new dict which maps field names to their corresponding values:
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
>>> p.__asdict__()
|
||||||
|
{'x': 11, 'y': 22}
|
||||||
|
|
||||||
|
.. method:: somenamedtuple.__replace__(field, value)
|
||||||
|
|
||||||
Return a new instance of the named tuple replacing the named *field* with a new *value*:
|
Return a new instance of the named tuple replacing the named *field* with a new *value*:
|
||||||
|
|
||||||
|
@ -447,20 +454,16 @@ an additonal method and an informational read-only attribute.
|
||||||
|
|
||||||
.. attribute:: somenamedtuple.__fields__
|
.. attribute:: somenamedtuple.__fields__
|
||||||
|
|
||||||
Return a tuple of strings listing the field names. This is useful for introspection,
|
Return a tuple of strings listing the field names. This is useful for introspection
|
||||||
for converting a named tuple instance to a dictionary, and for combining named tuple
|
and for creating new named tuple types from existing named tuples.
|
||||||
types to create new named tuple types:
|
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
>>> p.__fields__ # view the field names
|
>>> p.__fields__ # view the field names
|
||||||
('x', 'y')
|
('x', 'y')
|
||||||
>>> dict(zip(p.__fields__, p)) # convert to a dictionary
|
|
||||||
{'y': 22, 'x': 11}
|
|
||||||
|
|
||||||
>>> Color = NamedTuple('Color', 'red green blue')
|
>>> Color = NamedTuple('Color', 'red green blue')
|
||||||
>>> pixel_fields = ' '.join(Point.__fields__ + Color.__fields__) # combine fields
|
>>> Pixel = NamedTuple('Pixel', ' '.join(Point.__fields__ + Color.__fields__))
|
||||||
>>> Pixel = NamedTuple('Pixel', pixel_fields)
|
|
||||||
>>> Pixel(11, 22, 128, 255, 0)
|
>>> Pixel(11, 22, 128, 255, 0)
|
||||||
Pixel(x=11, y=22, red=128, green=255, blue=0)'
|
Pixel(x=11, y=22, red=128, green=255, blue=0)'
|
||||||
|
|
||||||
|
|
|
@ -18,19 +18,21 @@ def NamedTuple(typename, s, verbose=False):
|
||||||
(11, 22)
|
(11, 22)
|
||||||
>>> p.x + p.y # fields also accessable by name
|
>>> p.x + p.y # fields also accessable by name
|
||||||
33
|
33
|
||||||
>>> p # readable __repr__ with name=value style
|
>>> d = p.__asdict__() # convert to a dictionary
|
||||||
|
>>> d['x']
|
||||||
|
11
|
||||||
|
>>> Point(**d) # convert from a dictionary
|
||||||
Point(x=11, y=22)
|
Point(x=11, y=22)
|
||||||
>>> p.__replace__('x', 100) # __replace__() is like str.replace() but targets a named field
|
>>> p.__replace__('x', 100) # __replace__() is like str.replace() but targets a named field
|
||||||
Point(x=100, y=22)
|
Point(x=100, y=22)
|
||||||
>>> d = dict(zip(p.__fields__, p)) # use __fields__ to make a dictionary
|
|
||||||
>>> d['x']
|
|
||||||
11
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
field_names = tuple(s.replace(',', ' ').split()) # names separated by spaces and/or commas
|
field_names = tuple(s.replace(',', ' ').split()) # names separated by spaces and/or commas
|
||||||
if not ''.join((typename,) + field_names).replace('_', '').isalnum():
|
if not ''.join((typename,) + field_names).replace('_', '').isalnum():
|
||||||
raise ValueError('Type names and field names can only contain alphanumeric characters and underscores')
|
raise ValueError('Type names and field names can only contain alphanumeric characters and underscores')
|
||||||
|
if any(name.startswith('__') and name.endswith('__') for name in field_names):
|
||||||
|
raise ValueError('Field names cannot start and end with double underscores')
|
||||||
argtxt = repr(field_names).replace("'", "")[1:-1] # tuple repr without parens or quotes
|
argtxt = repr(field_names).replace("'", "")[1:-1] # tuple repr without parens or quotes
|
||||||
reprtxt = ', '.join('%s=%%r' % name for name in field_names)
|
reprtxt = ', '.join('%s=%%r' % name for name in field_names)
|
||||||
template = '''class %(typename)s(tuple):
|
template = '''class %(typename)s(tuple):
|
||||||
|
@ -41,7 +43,10 @@ def NamedTuple(typename, s, verbose=False):
|
||||||
return tuple.__new__(cls, (%(argtxt)s))
|
return tuple.__new__(cls, (%(argtxt)s))
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return '%(typename)s(%(reprtxt)s)' %% self
|
return '%(typename)s(%(reprtxt)s)' %% self
|
||||||
def __replace__(self, field, value):
|
def __asdict__(self, dict=dict, zip=zip):
|
||||||
|
'Return a new dict mapping field names to their values'
|
||||||
|
return dict(zip(%(field_names)r, self))
|
||||||
|
def __replace__(self, field, value, dict=dict, zip=zip):
|
||||||
'Return a new %(typename)s object replacing one field with a new value'
|
'Return a new %(typename)s object replacing one field with a new value'
|
||||||
return %(typename)s(**dict(zip(%(field_names)r, self) + [(field, value)])) \n''' % locals()
|
return %(typename)s(**dict(zip(%(field_names)r, self) + [(field, value)])) \n''' % locals()
|
||||||
for i, name in enumerate(field_names):
|
for i, name in enumerate(field_names):
|
||||||
|
|
|
@ -13,6 +13,7 @@ class TestNamedTuple(unittest.TestCase):
|
||||||
self.assertEqual(Point.__getitem__, tuple.__getitem__)
|
self.assertEqual(Point.__getitem__, tuple.__getitem__)
|
||||||
self.assertRaises(ValueError, NamedTuple, 'abc%', 'def ghi')
|
self.assertRaises(ValueError, NamedTuple, 'abc%', 'def ghi')
|
||||||
self.assertRaises(ValueError, NamedTuple, 'abc', 'def g%hi')
|
self.assertRaises(ValueError, NamedTuple, 'abc', 'def g%hi')
|
||||||
|
self.assertRaises(ValueError, NamedTuple, 'abc', '__def__ ghi')
|
||||||
NamedTuple('Point0', 'x1 y2') # Verify that numbers are allowed in names
|
NamedTuple('Point0', 'x1 y2') # Verify that numbers are allowed in names
|
||||||
|
|
||||||
def test_instance(self):
|
def test_instance(self):
|
||||||
|
@ -32,6 +33,7 @@ class TestNamedTuple(unittest.TestCase):
|
||||||
self.assert_('__weakref__' not in dir(p))
|
self.assert_('__weakref__' not in dir(p))
|
||||||
self.assertEqual(p.__fields__, ('x', 'y')) # test __fields__ attribute
|
self.assertEqual(p.__fields__, ('x', 'y')) # test __fields__ attribute
|
||||||
self.assertEqual(p.__replace__('x', 1), (1, 22)) # test __replace__ method
|
self.assertEqual(p.__replace__('x', 1), (1, 22)) # test __replace__ method
|
||||||
|
self.assertEqual(p.__asdict__(), dict(x=11, y=22)) # test __dict__ method
|
||||||
|
|
||||||
# verify that field string can have commas
|
# verify that field string can have commas
|
||||||
Point = NamedTuple('Point', 'x, y')
|
Point = NamedTuple('Point', 'x, y')
|
||||||
|
|
|
@ -47,6 +47,44 @@ class TestBasic(unittest.TestCase):
|
||||||
self.assertEqual(right, range(150, 400))
|
self.assertEqual(right, range(150, 400))
|
||||||
self.assertEqual(list(d), range(50, 150))
|
self.assertEqual(list(d), range(50, 150))
|
||||||
|
|
||||||
|
def test_maxlen(self):
|
||||||
|
self.assertRaises(ValueError, deque, 'abc', -2)
|
||||||
|
d = deque(range(10), maxlen=3)
|
||||||
|
self.assertEqual(repr(d), 'deque([7, 8, 9], maxlen=3)')
|
||||||
|
self.assertEqual(list(d), range(7, 10))
|
||||||
|
self.assertEqual(d, deque(range(10), 3))
|
||||||
|
d.append(10)
|
||||||
|
self.assertEqual(list(d), range(8, 11))
|
||||||
|
d.appendleft(7)
|
||||||
|
self.assertEqual(list(d), range(7, 10))
|
||||||
|
d.extend([10, 11])
|
||||||
|
self.assertEqual(list(d), range(9, 12))
|
||||||
|
d.extendleft([8, 7])
|
||||||
|
self.assertEqual(list(d), range(7, 10))
|
||||||
|
d = deque(xrange(200), maxlen=10)
|
||||||
|
d.append(d)
|
||||||
|
try:
|
||||||
|
fo = open(test_support.TESTFN, "wb")
|
||||||
|
print >> fo, d,
|
||||||
|
fo.close()
|
||||||
|
fo = open(test_support.TESTFN, "rb")
|
||||||
|
self.assertEqual(fo.read(), repr(d))
|
||||||
|
finally:
|
||||||
|
fo.close()
|
||||||
|
os.remove(test_support.TESTFN)
|
||||||
|
|
||||||
|
d = deque(range(10), maxlen=-1)
|
||||||
|
self.assertEqual(repr(d), 'deque([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])')
|
||||||
|
try:
|
||||||
|
fo = open(test_support.TESTFN, "wb")
|
||||||
|
print >> fo, d,
|
||||||
|
fo.close()
|
||||||
|
fo = open(test_support.TESTFN, "rb")
|
||||||
|
self.assertEqual(fo.read(), repr(d))
|
||||||
|
finally:
|
||||||
|
fo.close()
|
||||||
|
os.remove(test_support.TESTFN)
|
||||||
|
|
||||||
def test_comparisons(self):
|
def test_comparisons(self):
|
||||||
d = deque('xabc'); d.popleft()
|
d = deque('xabc'); d.popleft()
|
||||||
for e in [d, deque('abc'), deque('ab'), deque(), list(d)]:
|
for e in [d, deque('abc'), deque('ab'), deque(), list(d)]:
|
||||||
|
@ -254,7 +292,7 @@ class TestBasic(unittest.TestCase):
|
||||||
os.remove(test_support.TESTFN)
|
os.remove(test_support.TESTFN)
|
||||||
|
|
||||||
def test_init(self):
|
def test_init(self):
|
||||||
self.assertRaises(TypeError, deque, 'abc', 2);
|
self.assertRaises(TypeError, deque, 'abc', 2, 3);
|
||||||
self.assertRaises(TypeError, deque, 1);
|
self.assertRaises(TypeError, deque, 1);
|
||||||
|
|
||||||
def test_hash(self):
|
def test_hash(self):
|
||||||
|
@ -339,13 +377,13 @@ 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_pickle_recursive(self):
|
## def test_pickle_recursive(self):
|
||||||
d = deque('abc')
|
## d = deque('abc')
|
||||||
d.append(d)
|
## d.append(d)
|
||||||
for i in (0, 1, 2):
|
## for i in (0, 1, 2):
|
||||||
e = pickle.loads(pickle.dumps(d, i))
|
## e = pickle.loads(pickle.dumps(d, i))
|
||||||
self.assertNotEqual(id(d), id(e))
|
## self.assertNotEqual(id(d), id(e))
|
||||||
self.assertEqual(id(e), id(e[-1]))
|
## self.assertEqual(id(e), id(e[-1]))
|
||||||
|
|
||||||
def test_deepcopy(self):
|
def test_deepcopy(self):
|
||||||
mut = [10]
|
mut = [10]
|
||||||
|
@ -451,24 +489,24 @@ class TestSubclass(unittest.TestCase):
|
||||||
self.assertEqual(type(d), type(e))
|
self.assertEqual(type(d), type(e))
|
||||||
self.assertEqual(list(d), list(e))
|
self.assertEqual(list(d), list(e))
|
||||||
|
|
||||||
def test_pickle(self):
|
## def test_pickle(self):
|
||||||
d = Deque('abc')
|
## d = Deque('abc')
|
||||||
d.append(d)
|
## d.append(d)
|
||||||
|
##
|
||||||
e = pickle.loads(pickle.dumps(d))
|
## e = pickle.loads(pickle.dumps(d))
|
||||||
self.assertNotEqual(id(d), id(e))
|
## self.assertNotEqual(id(d), id(e))
|
||||||
self.assertEqual(type(d), type(e))
|
## self.assertEqual(type(d), type(e))
|
||||||
dd = d.pop()
|
## dd = d.pop()
|
||||||
ee = e.pop()
|
## ee = e.pop()
|
||||||
self.assertEqual(id(e), id(ee))
|
## self.assertEqual(id(e), id(ee))
|
||||||
self.assertEqual(d, e)
|
## self.assertEqual(d, e)
|
||||||
|
##
|
||||||
d.x = d
|
## d.x = d
|
||||||
e = pickle.loads(pickle.dumps(d))
|
## e = pickle.loads(pickle.dumps(d))
|
||||||
self.assertEqual(id(e), id(e.x))
|
## self.assertEqual(id(e), id(e.x))
|
||||||
|
##
|
||||||
d = DequeWithBadIter('abc')
|
## d = DequeWithBadIter('abc')
|
||||||
self.assertRaises(TypeError, pickle.dumps, d)
|
## self.assertRaises(TypeError, pickle.dumps, d)
|
||||||
|
|
||||||
def test_weakref(self):
|
def test_weakref(self):
|
||||||
d = deque('gallahad')
|
d = deque('gallahad')
|
||||||
|
|
|
@ -270,6 +270,8 @@ Core and builtins
|
||||||
Library
|
Library
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
- collections.deque() now supports a "maxlen" argument.
|
||||||
|
|
||||||
- itertools.count() is no longer bounded to LONG_MAX. Formerly, it raised
|
- itertools.count() is no longer bounded to LONG_MAX. Formerly, it raised
|
||||||
an OverflowError. Now, automatically shifts from ints to longs.
|
an OverflowError. Now, automatically shifts from ints to longs.
|
||||||
|
|
||||||
|
|
|
@ -83,10 +83,27 @@ typedef struct {
|
||||||
int leftindex; /* in range(BLOCKLEN) */
|
int leftindex; /* in range(BLOCKLEN) */
|
||||||
int rightindex; /* in range(BLOCKLEN) */
|
int rightindex; /* in range(BLOCKLEN) */
|
||||||
int len;
|
int len;
|
||||||
|
int maxlen;
|
||||||
long state; /* incremented whenever the indices move */
|
long state; /* incremented whenever the indices move */
|
||||||
PyObject *weakreflist; /* List of weak references */
|
PyObject *weakreflist; /* List of weak references */
|
||||||
} dequeobject;
|
} dequeobject;
|
||||||
|
|
||||||
|
/* The deque's size limit is d.maxlen. The limit can be zero or positive.
|
||||||
|
* If there is no limit, then d.maxlen == -1.
|
||||||
|
*
|
||||||
|
* After an item is added to a deque, we check to see if the size has grown past
|
||||||
|
* the limit. If it has, we get the size back down to the limit by popping an
|
||||||
|
* item off of the opposite end. The methods that can trigger this are append(),
|
||||||
|
* appendleft(), extend(), and extendleft().
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define TRIM(d, popfunction) \
|
||||||
|
if (d->maxlen != -1 && d->len > d->maxlen) { \
|
||||||
|
PyObject *rv = popfunction(d, NULL); \
|
||||||
|
assert(rv != NULL && d->len <= d->maxlen); \
|
||||||
|
Py_DECREF(rv); \
|
||||||
|
}
|
||||||
|
|
||||||
static PyTypeObject deque_type;
|
static PyTypeObject deque_type;
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
|
@ -95,9 +112,6 @@ deque_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||||
dequeobject *deque;
|
dequeobject *deque;
|
||||||
block *b;
|
block *b;
|
||||||
|
|
||||||
if (type == &deque_type && !_PyArg_NoKeywords("deque()", kwds))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
/* create dequeobject structure */
|
/* create dequeobject structure */
|
||||||
deque = (dequeobject *)type->tp_alloc(type, 0);
|
deque = (dequeobject *)type->tp_alloc(type, 0);
|
||||||
if (deque == NULL)
|
if (deque == NULL)
|
||||||
|
@ -117,54 +131,11 @@ deque_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||||
deque->len = 0;
|
deque->len = 0;
|
||||||
deque->state = 0;
|
deque->state = 0;
|
||||||
deque->weakreflist = NULL;
|
deque->weakreflist = NULL;
|
||||||
|
deque->maxlen = -1;
|
||||||
|
|
||||||
return (PyObject *)deque;
|
return (PyObject *)deque;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
|
||||||
deque_append(dequeobject *deque, PyObject *item)
|
|
||||||
{
|
|
||||||
deque->state++;
|
|
||||||
if (deque->rightindex == BLOCKLEN-1) {
|
|
||||||
block *b = newblock(deque->rightblock, NULL, deque->len);
|
|
||||||
if (b == NULL)
|
|
||||||
return NULL;
|
|
||||||
assert(deque->rightblock->rightlink == NULL);
|
|
||||||
deque->rightblock->rightlink = b;
|
|
||||||
deque->rightblock = b;
|
|
||||||
deque->rightindex = -1;
|
|
||||||
}
|
|
||||||
Py_INCREF(item);
|
|
||||||
deque->len++;
|
|
||||||
deque->rightindex++;
|
|
||||||
deque->rightblock->data[deque->rightindex] = item;
|
|
||||||
Py_RETURN_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
PyDoc_STRVAR(append_doc, "Add an element to the right side of the deque.");
|
|
||||||
|
|
||||||
static PyObject *
|
|
||||||
deque_appendleft(dequeobject *deque, PyObject *item)
|
|
||||||
{
|
|
||||||
deque->state++;
|
|
||||||
if (deque->leftindex == 0) {
|
|
||||||
block *b = newblock(NULL, deque->leftblock, deque->len);
|
|
||||||
if (b == NULL)
|
|
||||||
return NULL;
|
|
||||||
assert(deque->leftblock->leftlink == NULL);
|
|
||||||
deque->leftblock->leftlink = b;
|
|
||||||
deque->leftblock = b;
|
|
||||||
deque->leftindex = BLOCKLEN;
|
|
||||||
}
|
|
||||||
Py_INCREF(item);
|
|
||||||
deque->len++;
|
|
||||||
deque->leftindex--;
|
|
||||||
deque->leftblock->data[deque->leftindex] = item;
|
|
||||||
Py_RETURN_NONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
PyDoc_STRVAR(appendleft_doc, "Add an element to the left side of the deque.");
|
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
deque_pop(dequeobject *deque, PyObject *unused)
|
deque_pop(dequeobject *deque, PyObject *unused)
|
||||||
{
|
{
|
||||||
|
@ -239,6 +210,52 @@ 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_append(dequeobject *deque, PyObject *item)
|
||||||
|
{
|
||||||
|
deque->state++;
|
||||||
|
if (deque->rightindex == BLOCKLEN-1) {
|
||||||
|
block *b = newblock(deque->rightblock, NULL, deque->len);
|
||||||
|
if (b == NULL)
|
||||||
|
return NULL;
|
||||||
|
assert(deque->rightblock->rightlink == NULL);
|
||||||
|
deque->rightblock->rightlink = b;
|
||||||
|
deque->rightblock = b;
|
||||||
|
deque->rightindex = -1;
|
||||||
|
}
|
||||||
|
Py_INCREF(item);
|
||||||
|
deque->len++;
|
||||||
|
deque->rightindex++;
|
||||||
|
deque->rightblock->data[deque->rightindex] = item;
|
||||||
|
TRIM(deque, deque_popleft);
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyDoc_STRVAR(append_doc, "Add an element to the right side of the deque.");
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
deque_appendleft(dequeobject *deque, PyObject *item)
|
||||||
|
{
|
||||||
|
deque->state++;
|
||||||
|
if (deque->leftindex == 0) {
|
||||||
|
block *b = newblock(NULL, deque->leftblock, deque->len);
|
||||||
|
if (b == NULL)
|
||||||
|
return NULL;
|
||||||
|
assert(deque->leftblock->leftlink == NULL);
|
||||||
|
deque->leftblock->leftlink = b;
|
||||||
|
deque->leftblock = b;
|
||||||
|
deque->leftindex = BLOCKLEN;
|
||||||
|
}
|
||||||
|
Py_INCREF(item);
|
||||||
|
deque->len++;
|
||||||
|
deque->leftindex--;
|
||||||
|
deque->leftblock->data[deque->leftindex] = item;
|
||||||
|
TRIM(deque, deque_pop);
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
PyDoc_STRVAR(appendleft_doc, "Add an element to the left side of the deque.");
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
deque_extend(dequeobject *deque, PyObject *iterable)
|
deque_extend(dequeobject *deque, PyObject *iterable)
|
||||||
{
|
{
|
||||||
|
@ -266,6 +283,7 @@ deque_extend(dequeobject *deque, PyObject *iterable)
|
||||||
deque->len++;
|
deque->len++;
|
||||||
deque->rightindex++;
|
deque->rightindex++;
|
||||||
deque->rightblock->data[deque->rightindex] = item;
|
deque->rightblock->data[deque->rightindex] = item;
|
||||||
|
TRIM(deque, deque_popleft);
|
||||||
}
|
}
|
||||||
Py_DECREF(it);
|
Py_DECREF(it);
|
||||||
if (PyErr_Occurred())
|
if (PyErr_Occurred())
|
||||||
|
@ -303,6 +321,7 @@ deque_extendleft(dequeobject *deque, PyObject *iterable)
|
||||||
deque->len++;
|
deque->len++;
|
||||||
deque->leftindex--;
|
deque->leftindex--;
|
||||||
deque->leftblock->data[deque->leftindex] = item;
|
deque->leftblock->data[deque->leftindex] = item;
|
||||||
|
TRIM(deque, deque_pop);
|
||||||
}
|
}
|
||||||
Py_DECREF(it);
|
Py_DECREF(it);
|
||||||
if (PyErr_Occurred())
|
if (PyErr_Occurred())
|
||||||
|
@ -579,8 +598,8 @@ deque_nohash(PyObject *self)
|
||||||
static PyObject *
|
static PyObject *
|
||||||
deque_copy(PyObject *deque)
|
deque_copy(PyObject *deque)
|
||||||
{
|
{
|
||||||
return PyObject_CallFunctionObjArgs((PyObject *)(Py_Type(deque)),
|
return PyObject_CallFunction((PyObject *)(Py_Type(deque)), "Oi",
|
||||||
deque, NULL);
|
deque, ((dequeobject *)deque)->maxlen, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
PyDoc_STRVAR(copy_doc, "Return a shallow copy of a deque.");
|
PyDoc_STRVAR(copy_doc, "Return a shallow copy of a deque.");
|
||||||
|
@ -588,21 +607,22 @@ PyDoc_STRVAR(copy_doc, "Return a shallow copy of a deque.");
|
||||||
static PyObject *
|
static PyObject *
|
||||||
deque_reduce(dequeobject *deque)
|
deque_reduce(dequeobject *deque)
|
||||||
{
|
{
|
||||||
PyObject *dict, *result, *it;
|
PyObject *dict, *result, *aslist;
|
||||||
|
|
||||||
dict = PyObject_GetAttrString((PyObject *)deque, "__dict__");
|
dict = PyObject_GetAttrString((PyObject *)deque, "__dict__");
|
||||||
if (dict == NULL) {
|
if (dict == NULL)
|
||||||
PyErr_Clear();
|
PyErr_Clear();
|
||||||
dict = Py_None;
|
aslist = PySequence_List((PyObject *)deque);
|
||||||
Py_INCREF(dict);
|
if (aslist == NULL) {
|
||||||
}
|
|
||||||
it = PyObject_GetIter((PyObject *)deque);
|
|
||||||
if (it == NULL) {
|
|
||||||
Py_DECREF(dict);
|
Py_DECREF(dict);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
result = Py_BuildValue("O()ON", Py_Type(deque), dict, it);
|
if (dict == NULL)
|
||||||
Py_DECREF(dict);
|
result = Py_BuildValue("O(Oi)", Py_Type(deque), aslist, deque->maxlen);
|
||||||
|
else
|
||||||
|
result = Py_BuildValue("O(Oi)O", Py_Type(deque), aslist, deque->maxlen, dict);
|
||||||
|
Py_XDECREF(dict);
|
||||||
|
Py_DECREF(aslist);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -611,7 +631,7 @@ PyDoc_STRVAR(reduce_doc, "Return state information for pickling.");
|
||||||
static PyObject *
|
static PyObject *
|
||||||
deque_repr(PyObject *deque)
|
deque_repr(PyObject *deque)
|
||||||
{
|
{
|
||||||
PyObject *aslist, *result, *fmt;
|
PyObject *aslist, *result, *fmt; /*, *limit; */
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
i = Py_ReprEnter(deque);
|
i = Py_ReprEnter(deque);
|
||||||
|
@ -626,14 +646,17 @@ deque_repr(PyObject *deque)
|
||||||
Py_ReprLeave(deque);
|
Py_ReprLeave(deque);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
if (((dequeobject *)deque)->maxlen != -1)
|
||||||
fmt = PyString_FromString("deque(%r)");
|
fmt = PyString_FromFormat("deque(%%r, maxlen=%i)",
|
||||||
|
((dequeobject *)deque)->maxlen);
|
||||||
|
else
|
||||||
|
fmt = PyString_FromString("deque(%r)");
|
||||||
if (fmt == NULL) {
|
if (fmt == NULL) {
|
||||||
Py_DECREF(aslist);
|
Py_DECREF(aslist);
|
||||||
Py_ReprLeave(deque);
|
Py_ReprLeave(deque);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
result = PyString_Format(fmt, aslist);
|
result = PyString_Format(fmt, aslist);
|
||||||
Py_DECREF(fmt);
|
Py_DECREF(fmt);
|
||||||
Py_DECREF(aslist);
|
Py_DECREF(aslist);
|
||||||
Py_ReprLeave(deque);
|
Py_ReprLeave(deque);
|
||||||
|
@ -652,9 +675,7 @@ deque_tp_print(PyObject *deque, FILE *fp, int flags)
|
||||||
if (i != 0) {
|
if (i != 0) {
|
||||||
if (i < 0)
|
if (i < 0)
|
||||||
return i;
|
return i;
|
||||||
Py_BEGIN_ALLOW_THREADS
|
|
||||||
fputs("[...]", fp);
|
fputs("[...]", fp);
|
||||||
Py_END_ALLOW_THREADS
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -662,13 +683,9 @@ deque_tp_print(PyObject *deque, FILE *fp, int flags)
|
||||||
if (it == NULL)
|
if (it == NULL)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
Py_BEGIN_ALLOW_THREADS
|
|
||||||
fputs("deque([", fp);
|
fputs("deque([", fp);
|
||||||
Py_END_ALLOW_THREADS
|
|
||||||
while ((item = PyIter_Next(it)) != NULL) {
|
while ((item = PyIter_Next(it)) != NULL) {
|
||||||
Py_BEGIN_ALLOW_THREADS
|
|
||||||
fputs(emit, fp);
|
fputs(emit, fp);
|
||||||
Py_END_ALLOW_THREADS
|
|
||||||
emit = separator;
|
emit = separator;
|
||||||
if (PyObject_Print(item, fp, 0) != 0) {
|
if (PyObject_Print(item, fp, 0) != 0) {
|
||||||
Py_DECREF(item);
|
Py_DECREF(item);
|
||||||
|
@ -682,9 +699,11 @@ deque_tp_print(PyObject *deque, FILE *fp, int flags)
|
||||||
Py_DECREF(it);
|
Py_DECREF(it);
|
||||||
if (PyErr_Occurred())
|
if (PyErr_Occurred())
|
||||||
return -1;
|
return -1;
|
||||||
Py_BEGIN_ALLOW_THREADS
|
|
||||||
fputs("])", fp);
|
if (((dequeobject *)deque)->maxlen == -1)
|
||||||
Py_END_ALLOW_THREADS
|
fputs("])", fp);
|
||||||
|
else
|
||||||
|
fprintf(fp, "], maxlen=%d)", ((dequeobject *)deque)->maxlen);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -767,13 +786,19 @@ done:
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
deque_init(dequeobject *deque, PyObject *args, PyObject *kwds)
|
deque_init(dequeobject *deque, PyObject *args, PyObject *kwdargs)
|
||||||
{
|
{
|
||||||
PyObject *iterable = NULL;
|
PyObject *iterable = NULL;
|
||||||
|
int maxlen = -1;
|
||||||
|
char *kwlist[] = {"iterable", "maxlen", 0};
|
||||||
|
|
||||||
if (!PyArg_UnpackTuple(args, "deque", 0, 1, &iterable))
|
if (!PyArg_ParseTupleAndKeywords(args, kwdargs, "|Oi:deque", kwlist, &iterable, &maxlen))
|
||||||
return -1;
|
return -1;
|
||||||
|
if (maxlen < -1) {
|
||||||
|
PyErr_SetString(PyExc_ValueError, "maxlen must be -1 or greater");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
deque->maxlen = maxlen;
|
||||||
if (iterable != NULL) {
|
if (iterable != NULL) {
|
||||||
PyObject *rv = deque_extend(deque, iterable);
|
PyObject *rv = deque_extend(deque, iterable);
|
||||||
if (rv == NULL)
|
if (rv == NULL)
|
||||||
|
@ -828,7 +853,7 @@ static PyMethodDef deque_methods[] = {
|
||||||
};
|
};
|
||||||
|
|
||||||
PyDoc_STRVAR(deque_doc,
|
PyDoc_STRVAR(deque_doc,
|
||||||
"deque(iterable) --> deque object\n\
|
"deque(iterable[, maxlen]) --> deque object\n\
|
||||||
\n\
|
\n\
|
||||||
Build an ordered collection accessible from endpoints only.");
|
Build an ordered collection accessible from endpoints only.");
|
||||||
|
|
||||||
|
@ -1198,24 +1223,15 @@ static int
|
||||||
defdict_print(defdictobject *dd, FILE *fp, int flags)
|
defdict_print(defdictobject *dd, FILE *fp, int flags)
|
||||||
{
|
{
|
||||||
int sts;
|
int sts;
|
||||||
Py_BEGIN_ALLOW_THREADS
|
|
||||||
fprintf(fp, "defaultdict(");
|
fprintf(fp, "defaultdict(");
|
||||||
Py_END_ALLOW_THREADS
|
if (dd->default_factory == NULL)
|
||||||
if (dd->default_factory == NULL) {
|
|
||||||
Py_BEGIN_ALLOW_THREADS
|
|
||||||
fprintf(fp, "None");
|
fprintf(fp, "None");
|
||||||
Py_END_ALLOW_THREADS
|
|
||||||
}
|
|
||||||
else {
|
else {
|
||||||
PyObject_Print(dd->default_factory, fp, 0);
|
PyObject_Print(dd->default_factory, fp, 0);
|
||||||
}
|
}
|
||||||
Py_BEGIN_ALLOW_THREADS
|
|
||||||
fprintf(fp, ", ");
|
fprintf(fp, ", ");
|
||||||
Py_END_ALLOW_THREADS
|
|
||||||
sts = PyDict_Type.tp_print((PyObject *)dd, fp, 0);
|
sts = PyDict_Type.tp_print((PyObject *)dd, fp, 0);
|
||||||
Py_BEGIN_ALLOW_THREADS
|
|
||||||
fprintf(fp, ")");
|
fprintf(fp, ")");
|
||||||
Py_END_ALLOW_THREADS
|
|
||||||
return sts;
|
return sts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue