From 4715116df7cbcbe35814b2112bf38f48ff9b8f90 Mon Sep 17 00:00:00 2001 From: Raymond Hettinger Date: Fri, 27 Feb 2009 04:32:11 +0000 Subject: [PATCH] Update PEP based on latest version of recipe, based on integration testing, and based on newsgroup feedback. --- pep-0372.txt | 73 ++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 57 insertions(+), 16 deletions(-) diff --git a/pep-0372.txt b/pep-0372.txt index 517be190b..d9eeb3eb5 100644 --- a/pep-0372.txt +++ b/pep-0372.txt @@ -143,22 +143,19 @@ constructor? This behavior is consistent with existing implementations in Python, the PHP array and the hashmap in Ruby 1.9. -Why is there no ``odict.insert()``? - - There are few situations where you really want to insert a key at - 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: - - >>> d = odict([('a', 42), ('b', 23), ('c', 19)]) - >>> l = d.items() - >>> l.insert(1, ('x', 0)) - >>> odict(l) - collections.odict([('a', 42), ('x', 0), ('b', 23), ('c', 19)]) - -Is the ordered dict a dict subclass? +Is the ordered dict a dict subclass? Why? Yes. Like ``defaultdict``, ``odict`` subclasses ``dict``. + Being a dict subclass confers speed upon methods that aren't overridden + like ``__getitem__`` and ``__len__``. Also, being a dict gives the + most utility with tools that were expecting regular dicts (like the + json module). + +Do any limitations arise from subclassing dict? + + Yes. Since the API for dicts is different in Py2.x and Py3.x, the + odict API must also be different (i.e. Py2.6 needs to override + iterkeys, itervalues, and iteritems). Does ``odict.popitem()`` return a particular key/value pair? @@ -166,8 +163,8 @@ Does ``odict.popitem()`` return a particular key/value pair? 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). + The actual implementation is more efficient and pops directly + off of a sorted list of keys. Does odict support indexing, slicing, and whatnot? @@ -184,6 +181,50 @@ Does odict support alternate sort orders such as alphabetical? dbm) is likely a better fit. It would be a mistake to try to be all things to all users. +How well does odict work with the json module and PyYAML? + + For json, the good news is that json's encoder respects odict's iteration order: + + >>> items = [('one', 1), ('two', 2), ('three',3), ('four',4), ('five',5)] + >>> json.dumps(OrderedDict(items)) + '{"one": 1, "two": 2, "three": 3, "four": 4, "five": 5}' + + The bad news is that the object_hook for json decoders will pass in an + already built dictionary so that the order is lost before the object + hook sees it: + + >>> jtext = '{"one": 1, "two": 2, "three": 3, "four": 4, "five": 5}' + >>> json.loads(jtext, object_hook=OrderedDict) + OrderedDict({u'four': 4, u'three': 3, u'five': 5, u'two': 2, u'one': 1}) + + For PyYAML, a full round-trip is problem free: + + >>> ytext = yaml.dump(OrderedDict(items)) + >>> print ytext + !!python/object/apply:collections.OrderedDict + - - [one, 1] + - [two, 2] + - [three, 3] + - [four, 4] + - [five, 5] + + >>> yaml.load(ytext) + OrderedDict({'one': 1, 'two': 2, 'three': 3, 'four': 4, 'five': 5}) + +How does odict handle equality testing? + + Being a dict, one might expect equality tests to not care about order. For + an odict to dict comparison, this would be a necessity and it's probably + not wise to silently switch comparison modes based on the input types. + Also, some third-party tools that expect dict inputs may also expect the + comparison to not care about order. Accordingly, we decided to punt and + let the usual dict equality testing run without reference to internal + ordering. This should be documented clearly since different people will + have different expectations. If a use case does arise, it's not hard to + explicitly craft an order based comparison: + ``list(od1.items())==list(od2.items())``. + + Reference Implementation ========================