Hướng dẫn python append to ordereddict

Add the new item to the original items, sort, make a new dict:

>>> arr = {['a',1111],['b',2222],['f',3333]}
>>> arr = collections.OrderedDict[arr]
>>> new = ['c',4444]
>>> items = list[arr.items[]]
>>> items.append[new]
>>> items.sort[]
>>> arr = collections.OrderedDict[items]
>>> arr
OrderedDict[[['a', 1111], ['b', 2222], ['c', 4444], ['f', 3333]]]

Or a bit more involved option:

  • Subclass collections.OrderedDict
  • Using the move_to_end method as a guide create a new method that will traverse the doubly linked list; find the place to insert; then insert the new key - maybe bisect can be used here or some other doubly linked list sorted insertion algorithm
  • override the __setitem__ method and within it call the new method - or just replace the add-new-key-to-the-end code with the algorithm you came up with in the previous bullet.

Sorted dict that maintains key sorted order

I couldn't figure out how to make an OrderedDict subclass work - it has a number of attributes that get name mangled - only one or two methods need to be overridden and I didn't want to spend the time figuring out the name-mangling aspect.

So just copy the whole OrderedDict class from the source from here - to here into a separate module, so you can import it, and include these imports.

from _weakref import proxy as _proxy
from collections import _Link, _OrderedDictKeysView
from collections import _OrderedDictItemsView, _OrderedDictValuesView
import _collections_abc
from _weakref import proxy as _proxy
from reprlib import recursive_repr as _recursive_repr
from operator import itemgetter as _itemgetter, eq as _eq
import bisect

Then change the following in the class:

  • The class name of course to whatever you like. Following the class name is a docstring and some comments describing the class's behaviour - these should be updated.
    class SortOrderedDict[dict]:
  • Override the __setitem__ method. The following uses bisect to find the insertion order. Don't know if it is really warranted, it has to make a list of a dict keys view first but that part should be fast C code [? guessing here]
    def __setitem__[self, key, value,
                    dict_setitem=dict.__setitem__, proxy=_proxy, Link=_Link]:
        'od.__setitem__[i, y]  od[i]=y'
        # Setting a new item creates a new link in the linked list,
        # inserted at its key sorted position - uses less than comparisons,
        # and the inherited dictionary is updated with the new key/value pair.
        if key not in self:
            self.__map[key] = link = Link[]
            root = self.__root
            last = root.prev
            link.key = key
            curr = root.next
            if curr is root:    # first item!
                link.prev, link.next = last, root
                last.next = link
                root.prev = proxy[link]
            elif link.key < root.next.key:    # at the beginning?
                #print[f'{link.key} before {root.next.key}']
                soft_link = root.next
                link.prev, link.next = root, soft_link
                soft_link.prev = link
                root.next = link
            elif root.prev.key < link.key:    # at the end?
                #print[f'{link.key} at the end after {root.prev.key}']
                soft_link = root.prev
                link.prev, link.next = soft_link, root
                soft_link.next = link
                root.prev = proxy[link]
            else:    # in the middle somewhere - use bisect
                keys = list[self.keys[]]
                i = bisect.bisect_left[keys,key]
                right = self.__map[keys[i]]
                #print[f'{link.key} between {right.prev.key} and {right.key}']
                soft_link = right.prev
                link.prev,link.next = soft_link,right
                right.prev = link
                soft_link.next = link

        dict_setitem[self, key, value]
  • Add an update method - this class is a subclass of dict this overrides its update method forcing it to use __setitem__.
    def update[self,other]:
        try:
            other = other.items[]
        except AttributeError:
            pass
        for k,v in other:
            self[k] = v
  • Change this line update = __update = _collections_abc.MutableMapping.update to
    __update = update
  • In the __reduce__ method change the class name in for k in vars[OrderedDict[]]: to whatever you you named your class
    for k in vars[SortOrderedDict[]]:
  • Same thing in the __eq__ method. Change if isinstance[other, OrderedDict]: to
    if isinstance[other, SortOrderedDict]:

If using bisect doesn't seem worthwhile just traverse the linked list till the insertion point is found. [All the other changes listed above still apply]

    def __setitem__[self, key, value,
                    dict_setitem=dict.__setitem__, proxy=_proxy, Link=_Link]:
        'od.__setitem__[i, y]  od[i]=y'
        # Setting a new item creates a new link in the linked list,
        # inserted at its key sorted position - uses less than comparisons,
        # and the inherited dictionary is updated with the new key/value pair.
        if key not in self:
            self.__map[key] = link = Link[]
            root = self.__root
            last = root.prev
            link.key = key
            curr = root.next
            if curr is root:    # first item!
                link.prev, link.next = last, root
                last.next = link
                root.prev = proxy[link]
            # traverse the linked list; find sorted insertion point; insert
            while curr is not root:
                if link.key < curr.key:
                    soft_link = curr.prev
                    soft_link.next = link
                    link.prev = soft_link
                    link.next = curr
                    curr.prev = link
                    break
                elif curr.next is root:
                    link.prev, link.next = curr, root
                    curr.next = link
                    root.prev = proxy[link]
                    break
                curr = curr.next
        dict_setitem[self, key, value]

Usage

>>> arr = {['a',1111],['f',3333],['b',2222]}
>>> arr = SortOrderedDict[arr]
>>> arr
SortOrderedDict[[['a', 1111], ['b', 2222], ['f', 3333]]]
>>> other = {k:v for k,v in zip['tvsnpqkl',range[8]]}
>>> arr.update[other]
>>> arr
SortOrderedDict[[['a', 1111], ['b', 2222], ['f', 3333], ['k', 6], ['l', 7], ['n', 3], ['p', 4], ['q', 5], ['s', 2], ['t', 0], ['v', 1]]]
>>> b = SortOrderedDict[[['a',1111],['f',3333],['b',2222]]]
>>> b.update[other]
>>> arr == b
True
>>> b == arr
True
>>> 

Chủ Đề