[Python-checkins] r69992 - peps/trunk/pep-0372.txt
raymond.hettinger
python-checkins at python.org
Thu Feb 26 13:38:35 CET 2009
Author: raymond.hettinger
Date: Thu Feb 26 13:38:29 2009
New Revision: 69992
Log:
Join this PEP (with Armin's blessing) and move it forward
from where it's been sitting for a while. Update the text
for Py2.7 and Py3.1. Link to a new implementation that
sticks with the basic dict API and no new methods.
Modified:
peps/trunk/pep-0372.txt
Modified: peps/trunk/pep-0372.txt
==============================================================================
--- peps/trunk/pep-0372.txt (original)
+++ peps/trunk/pep-0372.txt Thu Feb 26 13:38:29 2009
@@ -3,11 +3,12 @@
Version: $Revision$
Last-Modified: $Date$
Author: Armin Ronacher <armin.ronacher at active-4.com>
+ Raymond Hettinger <python at rcn.com>
Status: Draft
Type: Standards Track
Content-Type: text/x-rst
Created: 15-Jun-2008
-Python-Version: 2.6, 3.0
+Python-Version: 2.7, 3.1
Post-History:
@@ -73,7 +74,7 @@
provide an ordered dictionary. [1]_
- Code ported from other programming languages such as PHP often
- depends on a ordered dict. Having an implementation of an
+ depends on an ordered dict. Having an implementation of an
ordering-preserving dictionary in the standard library could ease
the transition and improve the compatibility of different libraries.
@@ -82,12 +83,12 @@
================
The ordered dict API would be mostly compatible with dict and existing
-ordered dicts. (Note: this PEP refers to the Python 2.x dictionary
-API; the transfer to the 3.x API is trivial.)
+ordered dicts. Note: this PEP refers to the 2.7 and 3.0 dictionary
+API as described in collections.Mapping abstract base class.
The constructor and ``update()`` both accept iterables of tuples as
-well as mappings like a dict does. The ordering however is preserved
-for the first case:
+well as mappings like a dict does. Unlike a regular dictionary,
+the insertion order is preserved.
>>> d = odict([('a', 'b'), ('c', 'd')])
>>> d.update({'foo': 'bar'})
@@ -95,11 +96,11 @@
collections.odict([('a', 'b'), ('c', 'd'), ('foo', 'bar')])
If ordered dicts are updated from regular dicts, the ordering of new
-keys is of course undefined again unless ``sort()`` is called.
+keys is of course undefined.
All iteration methods as well as ``keys()``, ``values()`` and
-``items()`` return the values ordered by the time the key-value pair
-was inserted:
+``items()`` return the values ordered by the time the key was
+first inserted:
>>> d['spam'] = 'eggs'
>>> d.keys()
@@ -111,80 +112,9 @@
New methods not available on dict:
-``odict.byindex(index)``
- Returns the key/value pair for an index, that is, the "position" of a key in
- the ordered dict. 0 is the first key/value pair, -1 the last.
-
- >>> d.byindex(2)
- ('foo', 'bar')
-
- If there is no key for index an `IndexError` is raised. Slices are not
- supported.
-
-``odict.index(key)``
- Returns the index of a key. If the key does not exist, a `ValueError` is
- raised.
-
-``odict.sort(cmp=None, key=None, reverse=False)``
- Sorts the odict in place by cmp or key. This works exactly like
- ``list.sort()``, but the comparison functions are passed a key/value tuple,
- not only the value.
-
- >>> d = odict([(42, 1), (1, 4), (23, 7)]) d.sort() d
- collections.odict([(1, 4), (23, 7), (42, 1)])
-
-``odict.reverse()``
- Reverses the odict in place.
-
``odict.__reversed__()``
Supports reverse iteration by key.
-``odict.__eq__()`` / ``odict.__ne__()``
- Compares the odict to another object. If it's compared to another
- odict the ordering of items is taken into account, otherwise only
- the keys and values.
-
-``odict.__cmp__()``
- Ordered dicts are sorted by their items. ``cmp(od1, od2)`` is
- equivalent to ``cmp(od1.items(), od2.items())`` if both ``od1``
- and ``od2`` are ordered dicts. Otherwise the regular dict comparison
- kicks in.
-
-
-Python 3 Version
-================
-
-The Python 3 version of the ``odict`` returns dictionary views rather
-than lists for ``odict.keys()``, ``odict.values()`` and
-``odict.items()``. The keys view is equivalent to a regular keys view
-but supports the following extra or changed operations:
-
-``odict_keys.__getitem__(index)``
-
- Returns the key for an index. This is equivalent to
- ``odict.byindex(index)``.
-
-``odict_keys.index(key)``
-
- Returns the index for a key. This exists for compatibility with
- the ``Sequence`` abstract base class and is equivalent to
- ``odict.index(key)``.
-
-``odict_keys.__iter__()``
-
- Has the same semantics as ``odict.__iter__()``.
-
-``odict_keys.__reversed__()``
-
- Has the same semantics as ``odict.__reversed__()``.
-
-``odict_keys.__cmp__()`` / ``odict_keys.__eq__()`` /
-``odict_keys.__ne__()``
-
- Same semantics as the equivalent ``odict`` operation. E.g.: when
- compared to another odict keys view the ordering is taken into
- account.
-
Questions and Answers
=====================
@@ -205,7 +135,7 @@
The same as for regular dicts: The latter item overrides the
former. This has the side-effect that the position of the first
- key is used because the key is actually overwritten:
+ key is used because only the value is actually overwritten:
>>> odict([('a', 1), ('b', 2), ('a', 3)])
collections.odict([('a', 3), ('b', 2)])
@@ -216,7 +146,7 @@
Why is there no ``odict.insert()``?
There are few situations where you really want to insert a key at
- an specified index. To avoid API complication, the proposed
+ a specified index. To avoid API complication, the proposed
solution for this situation is creating a list of items,
manipulating that and converting it back into an odict:
@@ -230,31 +160,60 @@
Yes. Like ``defaultdict``, ``odict`` subclasses ``dict``.
-Does ``odict.pop()`` support list-like popping of items?
+Does ``odict.popitem()`` return a particular key/value pair?
- No. Neither ``odict.__getitem__()`` nor ``odict.pop()`` support
- retrieving or deleting items by index. Slices are not supported
- either. This would introduce ambiguities if integers or slice
- objects are used as dict keys.
+ Yes. It pops-off the most recently inserted new key and its
+ corresponding value. This corresponds to the usual LIFO behavior
+ exhibited by traditional push/pop pairs. It is semantically
+ equivalent to ``k=list(od)[-1]; v=od[k]; del od[k]; return (k,v)``.
+ The actual implementation is more efficient. It is O(n log n)
+ on the first call, any successive calls are O(1).
+
+Does odict support indexing, slicing, and whatnot?
As a matter of fact, ``odict`` does not implement the ``Sequence``
- interface.
+ interface. Rather, it is a ``MutableMapping`` that remembers
+ the order of key insertion. The only sequence-like addition is
+ automatic support for ``reversed``.
+Does odict support alternate sort orders such as alphabetical?
-Example Implementation
-======================
+ No. Those wanting different sort orders really need to be using another
+ technique. The odict is all about recording insertion order. If any
+ other order is of interest, then another structure (like an in-memory
+ dbm) is likely a better fit. It would be a mistake to try to be all
+ things to all users.
-A poorly performing example implementation of the odict written in
-Python is available:
+Reference Implementation
+========================
- `odict.py <http://dev.pocoo.org/hg/sandbox/raw-file/tip/odict.py>`_
+A proposed version is at:
-The version for ``collections`` should be implemented in C and use a
-linked list internally.
+ `OrderedDict recipe <http://code.activestate.com/recipes/576669/>`_
+
+The proposed version has several merits:
+
+* Strict compliance with the MutableMapping API and no new methods
+ so that the learning curve is near zero. It is simply a dictionary
+ that remembers insertion order.
+
+* Generally good performance. The big-oh times are the same as regular
+ dictionaries except for the cost of a single sort prior to the
+ first ordered retrieval (via *__iter__* or somesuch).
+
+* Key insertion and deletion is O(1). The work of organizing keys into
+ correct order is deferred to the end (instead of trying to maintain
+ sorted list, linked list, or btree as the dict is built-up). This
+ corresponds to typical use patterns (read-in ordered key/value pairs,
+ make modifications, and then write them back out in insertion order)
+ and it takes advantage of Python's highly efficient built-in sort.
+
+* The code runs without modification on Py2.6, Py2.7, Py3.0, and Py3.1.
Other implementations of ordered dicts in various Python projects or
standalone libraries, that inspired the API proposed here, are:
+- `odict in Python`_
- `odict in Babel`_
- `OrderedDict in Django`_
- `The odict module`_
@@ -262,7 +221,7 @@
- `StableDict`_
- `Armin Rigo's OrderedDict`_
-
+.. _odict in Python: http://dev.pocoo.org/hg/sandbox/raw-file/tip/odict.py
.. _odict in Babel: http://babel.edgewall.org/browser/trunk/babel/util.py?rev=374#L178
.. _OrderedDict in Django:
http://code.djangoproject.com/browser/django/trunk/django/utils/datastructures.py?rev=7140#L53
More information about the Python-checkins
mailing list