Why are there no ordered dictionaries?

Bengt Richter bokr at oz.net
Thu Dec 1 20:57:44 EST 2005


On 1 Dec 2005 03:53:27 -0800, "Fuzzyman" <fuzzyman at gmail.com> wrote:

>Hmmm... it would be interesting to see if these tests can be used with
>odict.
I assume you are referring to the pytest tests I posted, though I would
need some of the context you snipped to me more sure ;-)

Anyway, with some changes[1], they can.

>
>The odict implementation now has full functionality by the way.
>
The one at
    http://www.voidspace.org.uk/cgi-bin/voidspace/downman.py?file=odict.py
? It seems to be unchanged.


>Optimisations to follow and maybe a few *minor* changes.
>

Anyway, test of the above results in:

 C:\UTIL\\..\py.test  test
 ============================= test process starts =============================
 testing-mode: inprocess
 executable:   d:\python-2.4b1\mingw\python24.exe  (2.4.0-beta-1)
 using py lib: D:\bpy24\py-0.8.0-alpha2\py <rev unknown>
 
 test\test_odicts.py[28] FF...FF..FFFF..FFF..........
 test\test_odicts_candidate.py[0]
 
 _______________________________________________________________________________
 ___________________________ entrypoint: test_sanity ___________________________
 
     def test_sanity():
         d = CandidateDict()
         assert d.keys() == []
         assert d.values() == []
         assert d.items() == []
         assert d == d
         d = CandidateDict([(k,i*100) for i,k in enumerate('abcde')])
 E       assert repr(d) == "%s([('a', 0), ('b', 100), ('c', 200), ('d', 300), ('e', 400)])"%Can
 dateDict.__name__
 >       assert "{'a': 0, 'b': 100, 'c': 200, 'd': 300, 'e': 400}" == ("%s([('a', 0), ('b', 100
  ('c', 200), ('d', 300), ('e', 400)])" % CandidateDict.__name__)
          +  where "{'a': 0, 'b': 100, 'c': 200, 'd': 300, 'e': 400}" = repr({'a': 0, 'b': 100,
 c': 200, 'd': 300, 'e': 400})
 
 [C:\pywk\ut\test\test_odicts.py:19]
 _______________________________________________________________________________
Apparently you are still using plain dict repr format, not a format that could (ideally) be exec'd
to reconstitute the thing repr'd.

 >>> from  clp.odict import OrderedDict as CandidateDict
 >>> CandidateDict.__name__
 'OrderedDict'
 >>> repr(CandidateDict())
 '{}'

vs

 >>> from  ut.creordict import Creordict as CandidateDict
 >>> CandidateDict.__name__
 'Creordict'
 >>> repr(CandidateDict())
 'Creordict([])'

Since the test uses CandidateDict.__name__, it should work either way.

 __________________________ entrypoint: test___init__ __________________________
 
     def test___init__():
         d = CandidateDict()
         assert d.keys() == []
         assert d.values() == []
         assert d.items() == []
         assert d == d
         d = CandidateDict([(k,i*100) for i,k in enumerate('abcde')])
 E       assert repr(d) == "%s(%r)"% (CandidateDict.__name__, d.items())
 >       assert "{'a': 0, 'b': 100, 'c': 200, 'd': 300, 'e': 400}" == ('%s(%r)' % ('OrderedDict
  [('a', 0), ('b', 100), ('c', 200), ('d', 300), ('e', 400)]))
          +  where "{'a': 0, 'b': 100, 'c': 200, 'd': 300, 'e': 400}" = repr({'a': 0, 'b': 100,
 c': 200, 'd': 300, 'e': 400})
 
 [C:\pywk\ut\test\test_odicts.py:32]
 _______________________________________________________________________________
(Duplicate from test_sanity)

 ________________________ entrypoint: test___delitem__ _________________________
 
     def test___delitem__():
         d = CandidateDict([(k,i*100) for i,k in enumerate('abcde')])
         print mkresult('abcde')
         assert d == mkresult('abcde')
         ditems = d.items()
         print d['b']
         print d.items()
         del d['b']
         assert d ==  mkresult('acde')
         del d['a']
         assert d ==  mkresult('cde')
         del d['e']
         assert d ==  mkresult('cd')
         d = CandidateDict([(k,i*100) for i,k in enumerate('abcde')])
 >       del d[1:3]
 
 [C:\pywk\ut\test\test_odicts.py:70]
 _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
 
     def __delitem__(self, key):
         """
         >>> d = OrderedDict(((1, 3), (3, 2), (2, 1)))
         >>> del d[3]
         >>> d
         {1: 3, 2: 1}
         >>> del d[3]
         Traceback (most recent call last):
         KeyError: 3
         """
         # do the dict.__delitem__ *first* as it raises
         # the more appropriate error
 E       dict.__delitem__(self, key)
 >       TypeError: unhashable type
 
 [c:\pywk\clp\odict.py:137]
 - - - - - - - - - - -  test___delitem__: recorded stdout - - - - - - - - - - -
 {'a': 0, 'b': 100, 'c': 200, 'd': 300, 'e': 400}
 100
 [('a', 0), ('b', 100), ('c', 200), ('d', 300), ('e', 400)]
 
 _______________________________________________________________________________
You don't appear to accept slice operation syntax directly on the instance

 __________________________ entrypoint: test___repr__ __________________________
 
     def test___repr__():
 E       assert repr(CandidateDict()) == '%s([])'%CandidateDict.__name__
 >       assert '{}' == ('%s([])' % CandidateDict.__name__)
          +  where '{}' = repr({})
          +    where {} = CandidateDict()
 
 [C:\pywk\ut\test\test_odicts.py:78]
 _______________________________________________________________________________
 ________________________ entrypoint: test___getslice__ ________________________
 
     def test___getslice__():
         d = CandidateDict([(k,i*100) for i,k in enumerate('abcde')])
 E       assert d[2:4] == mkresult('cd')
 >       TypeError: unhashable type
 
 [C:\pywk\ut\test\test_odicts.py:102]
 _______________________________________________________________________________
Again, no slice op

 __________________________ entrypoint: test___add__ ___________________________
 
     def test___add__():
         d = CandidateDict([(k,i*100) for i,k in enumerate('abcde')])
 E       assert d+d == d
 >       TypeError: unsupported operand type(s) for +: 'OrderedDict' and 'OrderedDict'
 
 [C:\pywk\ut\test\test_odicts.py:110]
 _______________________________________________________________________________
No addition defined on OrderedDicts?

 __________________________ entrypoint: test_reverse ___________________________
 
     def test_reverse():
         d = CandidateDict([(k,i*100) for i,k in enumerate('abcde')])
 E       d.reverse()
 >       AttributeError: 'OrderedDict' object has no attribute 'reverse'
 
 [C:\pywk\ut\test\test_odicts.py:115]
 _______________________________________________________________________________
No reverse

 ___________________________ entrypoint: test_insert ___________________________
 
     def test_insert():
         d = CandidateDict([(k,i*100) for i,k in enumerate('abc')])
 E       d.insert(1, ('x', 101))
 >       AttributeError: 'OrderedDict' object has no attribute 'insert'
 
 [C:\pywk\ut\test\test_odicts.py:120]
 _______________________________________________________________________________
No insert

 ___________________________ entrypoint: test_items ____________________________
 
     def test_items():
         assert CandidateDict().items() == []
         d = CandidateDict([(k,i*100) for i,k in enumerate('abcde')])
         assert d.items() == [(k,i*100) for i,k in enumerate('abcde')]
 E       assert d.items[:] == [(k,i*100) for i,k in enumerate('abcde')]
 >       TypeError: unsubscriptable object
 
 [C:\pywk\ut\test\test_odicts.py:141]
 _______________________________________________________________________________
Only d.items() ?

 ____________________________ entrypoint: test_keys ____________________________
 
     def test_keys():
         assert CandidateDict().keys() == []
         d = CandidateDict([(k,i*100) for i,k in enumerate('abcde')])
         assert d.keys() == list('abcde')
 E       assert d.keys[:] == list('abcde')
 >       TypeError: unsubscriptable object
 
 [C:\pywk\ut\test\test_odicts.py:153]
 _______________________________________________________________________________
Only d.keys() ?

 ___________________________ entrypoint: test_values ___________________________
 
     def test_values():
         assert CandidateDict().values() == []
         d = CandidateDict([(k,i*100) for i,k in enumerate('abcde')])
         assert d.values() == [i*100 for i,k in enumerate('abcde')]
 E       assert d.values[:] == [i*100 for i,k in enumerate('abcde')]
 >       TypeError: unsubscriptable object
 
 [C:\pywk\ut\test\test_odicts.py:170]
 _______________________________________________________________________________
Only d.values() ?

 ============ tests finished: 17 passed, 11 failed in 1.36 seconds =============
 

[1] I changed the tests to use a CandidateDict alias, and eliminated a couple of checks
on internal state, so as to allow different implementations to be tested, using
CandidateDict.__name__ to synthesize alternate expected representation strings.

I made the test selectable if run interactively instead of directly via pytest:

 [17:43] C:\pywk\ut>py24 test\test_odicts.py
 Enter selection [1..2] (q to do nothing)
 for importing candidate odicts
   1: from ut.creordict import Creordict as CandidateDict
   2: from clp.odict import OrderedDict as CandidateDict
 > 1

     "from ut.creordict import Creordict as CandidateDict"

 is selected. Type "Yes" exactly (w/o quotes) to accept: Yes
 ============================= test process starts =============================
 testing-mode: inprocess
 executable:   d:\python-2.4b1\mingw\python24.exe  (2.4.0-beta-1)
 using py lib: D:\bpy24\py-0.8.0-alpha2\py <rev unknown>

 test\test_odicts.py[28] ............................

 ================== tests finished: 28 passed in 1.03 seconds ==================

Not sure they way I did it is a good idea, but anyway I can just add a little
boilerplate at the bottom of a test and a text file with the choices (which are
one-line sources that get written to test_... so the test can do from test_... import CandidateDict
and have the chosen class by the name it uses for both.

Regards,
Bengt Richter



More information about the Python-list mailing list