summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/library/collections.rst17
-rw-r--r--Lib/collections.py23
-rw-r--r--Lib/functools.py2
-rw-r--r--Lib/test/test_collections.py14
-rw-r--r--Misc/NEWS3
5 files changed, 51 insertions, 8 deletions
diff --git a/Doc/library/collections.rst b/Doc/library/collections.rst
index 454817b..1cc4097 100644
--- a/Doc/library/collections.rst
+++ b/Doc/library/collections.rst
@@ -793,6 +793,23 @@ the items are returned in the order their keys were first added.
(key, value) pair. The pairs are returned in LIFO order if *last* is true
or FIFO order if false.
+ .. method:: move_to_end(key, last=True)
+
+ Move an existing *key* to either end of an ordered dictionary. The item
+ is moved to the right end if *last* is true (the default) or to the
+ beginning if *last* is false. Raises :exc:`KeyError` if the *key* does
+ not exist::
+
+ >>> d = OrderedDict.fromkeys('abcde')
+ >>> d.move_to_end('b')
+ >>> ''.join(d.keys)
+ 'acdeb'
+ >>> d.move_to_end('b', 0)
+ >>> ''.join(d.keys)
+ 'bacde'
+
+ .. versionadded:: 3.2
+
In addition to the usual mapping methods, ordered dictionaries also support
reverse iteration using :func:`reversed`.
diff --git a/Lib/collections.py b/Lib/collections.py
index c3c51d1..00886ef 100644
--- a/Lib/collections.py
+++ b/Lib/collections.py
@@ -173,18 +173,29 @@ class OrderedDict(dict, MutableMapping):
def __del__(self):
self.clear() # eliminate cyclical references
- def _renew(self, key, PREV=0, NEXT=1):
- 'Fast version of self[key]=self.pop(key). Private method for internal use.'
+ def move_to_end(self, key, last=True, PREV=0, NEXT=1):
+ '''Move an existing element to the end (or beginning if last==False).
+
+ Raises KeyError if the element does not exist.
+ When last=True, acts like a fast version of self[key]=self.pop(key).
+
+ '''
link = self.__map[key]
link_prev = link[PREV]
link_next = link[NEXT]
link_prev[NEXT] = link_next
link_next[PREV] = link_prev
root = self.__root
- last = root[PREV]
- link[PREV] = last
- link[NEXT] = root
- last[NEXT] = root[PREV] = link
+ if last:
+ last = root[PREV]
+ link[PREV] = last
+ link[NEXT] = root
+ last[NEXT] = root[PREV] = link
+ else:
+ first = root[NEXT]
+ link[PREV] = root
+ link[NEXT] = first
+ root[NEXT] = first[PREV] = link
################################################################################
diff --git a/Lib/functools.py b/Lib/functools.py
index b2df390..a723f66 100644
--- a/Lib/functools.py
+++ b/Lib/functools.py
@@ -127,7 +127,7 @@ def lru_cache(maxsize=100):
len=len, KeyError=KeyError):
cache = OrderedDict() # ordered least recent to most recent
cache_popitem = cache.popitem
- cache_renew = cache._renew
+ cache_renew = cache.move_to_end
kwd_mark = object() # separate positional and keyword args
lock = Lock()
diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py
index 75d660d..7beb061 100644
--- a/Lib/test/test_collections.py
+++ b/Lib/test/test_collections.py
@@ -973,7 +973,19 @@ class TestOrderedDict(unittest.TestCase):
od['a'] = 1
self.assertEqual(list(od.items()), [('b', 2), ('a', 1)])
-
+ def test_move_to_end(self):
+ od = OrderedDict.fromkeys('abcde')
+ self.assertEqual(list(od), list('abcde'))
+ od.move_to_end('c')
+ self.assertEqual(list(od), list('abdec'))
+ od.move_to_end('c', 0)
+ self.assertEqual(list(od), list('cabde'))
+ od.move_to_end('c', 0)
+ self.assertEqual(list(od), list('cabde'))
+ od.move_to_end('e')
+ self.assertEqual(list(od), list('cabde'))
+ with self.assertRaises(KeyError):
+ od.move_to_end('x')
class GeneralMappingTests(mapping_tests.BasicTestMappingProtocol):
type2test = OrderedDict
diff --git a/Misc/NEWS b/Misc/NEWS
index d2d35d0..262983b 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -13,6 +13,9 @@ Core and Builtins
Library
-------
+- collections.OrderedDict now supports a new method for repositioning
+ keys to either end.
+
- Issue #9754: Similarly to assertRaises and assertRaisesRegexp, unittest
test cases now also have assertWarns and assertWarnsRegexp methods to
check that a given warning type was triggered by the code under test.