How can this function be rewritten to implement OrderedDict?
You could use the new OrderedDict
dict
subclass which was added to the standard library's collections
module in version 2.7✶. Actually what you need is an Ordered
+defaultdict
combination which doesn't exist — but it's possible to create one by subclassing OrderedDict
as illustrated below:
✶ If your version of Python doesn't have OrderedDict
, you should be able use Raymond Hettinger's Ordered Dictionary for Py2.4 ActiveState recipe as the base class instead.
import collections
class OrderedDefaultdict(collections.OrderedDict):
""" A defaultdict with OrderedDict as its base class. """
def __init__(self, default_factory=None, *args, **kwargs):
if not (default_factory is None or callable(default_factory)):
raise TypeError('first argument must be callable or None')
super(OrderedDefaultdict, self).__init__(*args, **kwargs)
self.default_factory = default_factory # called by __missing__()
def __missing__(self, key):
if self.default_factory is None:
raise KeyError(key,)
self[key] = value = self.default_factory()
return value
def __reduce__(self): # Optional, for pickle support.
args = (self.default_factory,) if self.default_factory else tuple()
return self.__class__, args, None, None, iter(self.items())
def __repr__(self): # Optional.
return '%s(%r, %r)' % (self.__class__.__name__, self.default_factory, self.items())
def simplexml_load_file(file):
from lxml import etree
tree = etree.parse(file)
root = tree.getroot()
def xml_to_item(el):
item = el.text or None
child_dicts = OrderedDefaultdict(list)
for child in el.getchildren():
child_dicts[child.tag].append(xml_to_item(child))
return collections.OrderedDict(child_dicts) or item
def xml_to_dict(el):
return {el.tag: xml_to_item(el)}
return xml_to_dict(root)
x = simplexml_load_file('routines/test.xml')
print(x)
for y in x['root']:
print(y)
The output produced from your test XML file looks like this:
{'root':
OrderedDict(
[('a', ['1']),
('aa', [OrderedDict([('b', [OrderedDict([('c', ['2'])]), '2'])])]),
('aaa', ['3']),
('aaaa', [OrderedDict([('bb', ['4'])])]),
('aaaaa', ['5'])
]
)
}
a
aa
aaa
aaaa
aaaaa
Which I think is close to what you want.
Minor update:
Added a __reduce__()
method which will allow the instances of the class to be pickled and unpickled properly. This wasn't necessary for this question, but came up in a similar one.
How to implement an ordered, default dict?
The following (using a modified version of this recipe) works for me:
from collections import OrderedDict, Callable
class DefaultOrderedDict(OrderedDict):
# Source: http://stackoverflow.com/a/6190500/562769
def __init__(self, default_factory=None, *a, **kw):
if (default_factory is not None and
not isinstance(default_factory, Callable)):
raise TypeError('first argument must be callable')
OrderedDict.__init__(self, *a, **kw)
self.default_factory = default_factory
def __getitem__(self, key):
try:
return OrderedDict.__getitem__(self, key)
except KeyError:
return self.__missing__(key)
def __missing__(self, key):
if self.default_factory is None:
raise KeyError(key)
self[key] = value = self.default_factory()
return value
def __reduce__(self):
if self.default_factory is None:
args = tuple()
else:
args = self.default_factory,
return type(self), args, None, None, self.items()
def copy(self):
return self.__copy__()
def __copy__(self):
return type(self)(self.default_factory, self)
def __deepcopy__(self, memo):
import copy
return type(self)(self.default_factory,
copy.deepcopy(self.items()))
def __repr__(self):
return 'OrderedDefaultDict(%s, %s)' % (self.default_factory,
OrderedDict.__repr__(self))
Ordered Dictionary in Python
The moment you write dict = {'a':1, 'q':1, 'l':2, 'm':1, 'u':1, 'i':1}
, you already have lost the order of the elements. The ways to keep it:
a. Create OrderedDict
from your data in the first place.
b. Convert your dict
to a list of tuples and sort in a way you want, then create OrderedDict
from it.
All in all, from your question it is not clear what you want to preserve. If it is generated "randomly" then who cares, if there is some logic behind it, then use this logic to recreate that order and create OrderedDict
using it. If there is something happening behind the scenes which creates this dict from some input data, then, alas, the order in which you see it is not the order in which it has been created.
PS And don't call your dict dict
.
Use of Ordered Dict
Since python 3.7 Dictionary order is guaranteed to be insertion order.
check this answer for similar question link
subclassing from OrderedDict and defaultdict
Inheriting from OrderedDict
as in the answer you linked to is the simplest way. It is more work to implement an ordered store than it is to get default values from a factory function.
All you need to implement for defaultdict
is a bit of custom __init__
logic and the extremely simple __missing__
.
If you instead inherit from defaultdict
, you have to delegate to or re-implement at least __setitem__
, __delitem__
and __iter__
to reproduce the in-order operation. You still have to do setup work in __init__
, though you might be able to inherit or simply leave out some of the other methods depending on your needs.
Take a look at the original recipe or any of the others linked to from another Stack Overflow question for what that would entail.
OrderedDict Isn't Ordered?
collections.OrderedDict
keeps track of the order in which elements were added to it. This would work fine in a loop:
c = collections.OrderedDict()
for a,b in zip('abc', (1,2,3)):
c[a] = b
However, the expression OrderedDict(a=1,b=2,c=3)
creates an OrderedDict
by passing several keyword arguments to its constructor. In Python 2.7, the order of keyword arguments is not guaranteed. If you want that, you'll have to move to Python 3.6, which implements PEP 468, Preserving the order of **kwargs in a function.
The
**kwargs
syntax in a function definition indicates that the interpreter should collect all keyword arguments that do not correspond to other named parameters. However, Python does not preserved the order in which those collected keyword arguments were passed to the function. In some contexts the order matters. This PEP dictates that the collected keyword arguments be exposed in the function body as an ordered mapping.
How do you retrieve items from a dictionary in the order that they're inserted?
The standard Python dict
does this by default if you're using CPython 3.6+ (or Python 3.7+ for any other implementation of Python).
On older versions of Python you can use collections.OrderedDict
.
Related Topics
List of Tables, Db Schema, Dump etc Using the Python SQLite3 API
Flask at First Run: Do Not Use the Development Server in a Production Environment
Is There a "Not Equal" Operator in Python
Catching an Exception While Using a Python 'With' Statement
Programmatically Searching Google in Python Using Custom Search
How to Check If Character in a String Is a Letter? (Python)
How to Implement _Getattribute_ Without an Infinite Recursion Error
Function Name Is Undefined in Python Class
Getting Started with the Python Debugger, Pdb
Concatenate Two Numpy Arrays Vertically
Get Raw Post Body in Python Flask Regardless of Content-Type Header
Python Datetime Object Show Wrong Timezone Offset
Call a Python Function from Jinja2
Why Don't Methods Have Reference Equality
List() Uses Slightly More Memory Than List Comprehension
Python-Requests Close Http Connection
Typeerror: Unsupported Operand Type(S) for /: 'Str' and 'Str'