จะเพิ่มองค์ประกอบที่จุดเริ่มต้นของ OrderDict ได้อย่างไร?


93

ฉันมีสิ่งนี้:

d1 = OrderedDict([('a', '1'), ('b', '2')])

ถ้าฉันทำสิ่งนี้:

d1.update({'c':'3'})

จากนั้นฉันจะได้รับสิ่งนี้:

OrderedDict([('a', '1'), ('b', '2'), ('c', '3')])

แต่ฉันต้องการสิ่งนี้:

[('c', '3'), ('a', '1'), ('b', '2')]

โดยไม่ต้องสร้างพจนานุกรมใหม่


3
ฉันคิดว่าคุณควรออกแบบโปรแกรมของคุณใหม่
Dmitry Zagorulkin

2
"OrderDict คือคำสั่งที่จำคำสั่งที่ใส่คีย์ครั้งแรก" @ZagorulkinDmitry ใช่เลย :) ( docs.python.org/2/library/… )
Markon

คำตอบ:


86

ไม่มีวิธีการในตัวในการทำสิ่งนี้ใน Python 2 หากคุณต้องการสิ่งนี้คุณต้องเขียนprepend()วิธีการ / ฟังก์ชั่นที่ทำงานบนOrderedDictภายในที่มีความซับซ้อน O (1)

สำหรับ Python 3.2 และใหม่กว่าคุณควรใช้move_to_endเมธอด เมธอดยอมรับlastอาร์กิวเมนต์ซึ่งระบุว่าองค์ประกอบจะถูกย้ายไปที่ด้านล่าง ( last=True) หรือด้านบน ( last=False) ของOrderedDict.

สุดท้ายหากคุณต้องการวิธีแก้ปัญหาที่รวดเร็วสกปรกและช้าคุณสามารถสร้างใหม่OrderedDictตั้งแต่ต้น

รายละเอียดสำหรับสี่โซลูชันที่แตกต่างกัน:


ขยายOrderedDictและเพิ่มวิธีการอินสแตนซ์ใหม่

from collections import OrderedDict

class MyOrderedDict(OrderedDict):

    def prepend(self, key, value, dict_setitem=dict.__setitem__):

        root = self._OrderedDict__root
        first = root[1]

        if key in self:
            link = self._OrderedDict__map[key]
            link_prev, link_next, _ = link
            link_prev[1] = link_next
            link_next[0] = link_prev
            link[0] = root
            link[1] = first
            root[1] = first[0] = link
        else:
            root[1] = first[0] = self._OrderedDict__map[key] = [root, first, key]
            dict_setitem(self, key, value)

การสาธิต:

>>> d = MyOrderedDict([('a', '1'), ('b', '2')])
>>> d
MyOrderedDict([('a', '1'), ('b', '2')])
>>> d.prepend('c', 100)
>>> d
MyOrderedDict([('c', 100), ('a', '1'), ('b', '2')])
>>> d.prepend('a', d['a'])
>>> d
MyOrderedDict([('a', '1'), ('c', 100), ('b', '2')])
>>> d.prepend('d', 200)
>>> d
MyOrderedDict([('d', 200), ('a', '1'), ('c', 100), ('b', '2')])

ฟังก์ชันสแตนด์อโลนที่จัดการกับOrderedDictวัตถุ

ฟังก์ชันนี้ทำสิ่งเดียวกันโดยยอมรับวัตถุ dict คีย์และค่า ฉันชอบชั้นเรียนเป็นการส่วนตัว:

from collections import OrderedDict

def ordered_dict_prepend(dct, key, value, dict_setitem=dict.__setitem__):
    root = dct._OrderedDict__root
    first = root[1]

    if key in dct:
        link = dct._OrderedDict__map[key]
        link_prev, link_next, _ = link
        link_prev[1] = link_next
        link_next[0] = link_prev
        link[0] = root
        link[1] = first
        root[1] = first[0] = link
    else:
        root[1] = first[0] = dct._OrderedDict__map[key] = [root, first, key]
        dict_setitem(dct, key, value)

การสาธิต:

>>> d = OrderedDict([('a', '1'), ('b', '2')])
>>> ordered_dict_prepend(d, 'c', 100)
>>> d
OrderedDict([('c', 100), ('a', '1'), ('b', '2')])
>>> ordered_dict_prepend(d, 'a', d['a'])
>>> d
OrderedDict([('a', '1'), ('c', 100), ('b', '2')])
>>> ordered_dict_prepend(d, 'd', 500)
>>> d
OrderedDict([('d', 500), ('a', '1'), ('c', 100), ('b', '2')])

ใช้OrderedDict.move_to_end()(Python> = 3.2)

งูหลาม 3.2 แนะนำOrderedDict.move_to_end()วิธี ใช้มันเราสามารถย้ายคีย์ที่มีอยู่ไปยังจุดสิ้นสุดของพจนานุกรมในเวลา O (1)

>>> d1 = OrderedDict([('a', '1'), ('b', '2')])
>>> d1.update({'c':'3'})
>>> d1.move_to_end('c', last=False)
>>> d1
OrderedDict([('c', '3'), ('a', '1'), ('b', '2')])

หากเราต้องการแทรกองค์ประกอบและย้ายไปด้านบนทั้งหมดในขั้นตอนเดียวเราสามารถใช้มันเพื่อสร้างprepend()กระดาษห่อหุ้มได้โดยตรง(ไม่ได้นำเสนอที่นี่)


สร้างใหม่OrderedDict- ช้า !!!

หากคุณไม่ต้องการทำเช่นนั้นและประสิทธิภาพไม่ใช่ปัญหาวิธีที่ง่ายที่สุดคือสร้างคำสั่งใหม่:

from itertools import chain, ifilterfalse
from collections import OrderedDict


def unique_everseen(iterable, key=None):
    "List unique elements, preserving order. Remember all elements ever seen."
    # unique_everseen('AAAABBBCCDAABBB') --> A B C D
    # unique_everseen('ABBCcAD', str.lower) --> A B C D
    seen = set()
    seen_add = seen.add
    if key is None:
        for element in ifilterfalse(seen.__contains__, iterable):
            seen_add(element)
            yield element
    else:
        for element in iterable:
            k = key(element)
            if k not in seen:
                seen_add(k)
                yield element

d1 = OrderedDict([('a', '1'), ('b', '2'),('c', 4)])
d2 = OrderedDict([('c', 3), ('e', 5)])   #dict containing items to be added at the front
new_dic = OrderedDict((k, d2.get(k, d1.get(k))) for k in \
                                           unique_everseen(chain(d2, d1)))
print new_dic

เอาต์พุต:

OrderedDict([('c', 3), ('e', 5), ('a', '1'), ('b', '2')])


โปรดทราบว่าหากcมีอยู่แล้วจะไม่อัปเดตค่าเก่า
jamylak

1
@IARI หากคุณอ้างถึงmove_to_endแล้วไม่มีแท็ก Python 3 ในคำถามใช้move_to_endงานได้เฉพาะใน Python 3.2+ เท่านั้น ฉันจะอัปเดตคำตอบของฉันเพื่อรวมโซลูชันที่ใช้ Python 3 ขอบคุณมากสำหรับการอัปเดต!
Ashwini Chaudhary

1
@AshwiniChaudhary เนื่องจาก Python 3 ได้เพิ่มไปแล้วmove_to_frontอาจจะดีกว่าที่จะใช้move_to_frontวิธีการแทนprependวิธีแยกต่างหาก? สิ่งนี้จะทำให้โค้ดของคุณพกพาสะดวกยิ่งขึ้นหากคุณต้องการรองรับทั้ง Python 2 และ Python 3 จากโค้ดเบสเดียวกัน
m000

1
เหตุผลเบื้องหลังdict_setitem=dict.__setitem__ในฐานะพาราprependคืออะไร? เหตุใดจึงควร / ผ่าน setter อื่น
Jir

2
ต้องมีจุดบกพร่องในordered_dict_prependด้านบน การโทรordered_dict_prepend(d, 'c', 100)สองครั้งและพยายามพิมพ์คำสั่งที่เป็นผลลัพธ์ (โดยการป้อนdในคอนโซลของ Python) ส่งผลให้กระบวนการ Python เก็บหน่วยความจำไว้ ทดสอบกับ Python 2.7.10
Piotr Dobrogost

15

แก้ไข (2019-02-03) โปรดทราบว่าคำตอบต่อไปนี้ใช้ได้กับ Python เวอร์ชันเก่าเท่านั้น เมื่อไม่นานมานี้OrderedDictมีการเขียนใหม่ใน C นอกจากนี้ยังสัมผัสแอตทริบิวต์ขีดล่างสองครั้งซึ่งขมวดคิ้ว

ฉันเพิ่งเขียนคลาสย่อยOrderedDictในโครงการของฉันเพื่อจุดประสงค์ที่คล้ายกัน ที่นี่เค้า

การดำเนินการแทรกเป็นเวลาคงที่เช่นกันO(1)(ไม่ต้องการให้คุณสร้างโครงสร้างข้อมูลใหม่) ซึ่งแตกต่างจากโซลูชันเหล่านี้ส่วนใหญ่

>>> d1 = ListDict([('a', '1'), ('b', '2')])
>>> d1.insert_before('a', ('c', 3))
>>> d1
ListDict([('c', 3), ('a', '1'), ('b', '2')])

ฉันได้รับTypeError: '_Link' object does not support indexingเมื่อใช้สิ่งนี้บน Python 3.4
RickardSjogren

1
นอกจากนี้ยังใช้ไม่ได้กับ python 3.5: AttributeError: วัตถุ 'ListDict' ไม่มีแอตทริบิวต์ '_OrderDict__map'
maggie

2
สิ่งนี้ใช้ไม่ได้อีกต่อไปเนื่องจากOrderedDictได้รับการเขียนใหม่ใน Cเมื่อ Python 3.5 และคลาสย่อยนี้ยอมรับข้อห้ามในการยุ่งเกี่ยวกับภายใน (จริง ๆ แล้วการย้อนกลับชื่อ mangling เพื่อเข้าถึงคุณสมบัติ __)
Two-Bit Alchemist

13

คุณต้องสร้างอินสแตนซ์ใหม่ของOrderedDictไฟล์. หากคีย์ของคุณไม่ซ้ำกัน:

d1=OrderedDict([("a",1),("b",2)])
d2=OrderedDict([("c",3),("d",99)])
both=OrderedDict(list(d2.items()) + list(d1.items()))
print(both)

#OrderedDict([('c', 3), ('d', 99), ('a', 1), ('b', 2)])

แต่ถ้าไม่ให้ระวังเพราะพฤติกรรมนี้อาจเป็นที่ต้องการหรือไม่ก็ได้สำหรับคุณ

d1=OrderedDict([("a",1),("b",2)])
d2=OrderedDict([("c",3),("b",99)])
both=OrderedDict(list(d2.items()) + list(d1.items()))
print(both)

#OrderedDict([('c', 3), ('b', 2), ('a', 1)])

3
ใน python3 เมธอด items จะไม่ส่งคืนรายการอีกต่อไป แต่เป็นมุมมองซึ่งทำหน้าที่เหมือนชุด ในกรณีนี้คุณจะต้องใช้ set union เนื่องจากการเชื่อมต่อกับ + จะไม่ทำงาน: dict (x.items () | y.items ())
The Demz

1
@TheDemz ฉันคิดว่า set union จะไม่รักษาคำสั่งซื้อจึงทำให้ลำดับสุดท้ายของรายการในผลลัพธ์OrderedDictไม่เสถียร?
สูงสุด

@max ใช่มันไม่เสถียร อ็อบเจ็กต์ที่ส่งคืนจาก dict.keys (), dict.values ​​() และ dict.items () เรียกว่ามุมมองพจนานุกรม เป็นลำดับที่ขี้เกียจซึ่งจะเห็นการเปลี่ยนแปลงในพจนานุกรม เพื่อบังคับให้มุมมองพจนานุกรมกลายเป็นรายการใช้แบบเต็ม (dictview) ดูวัตถุมุมมองพจนานุกรม docs.python.org/3.4/library/…
The Demz

6

ถ้าคุณรู้ว่าคุณต้องการคีย์ 'c' แต่ไม่ทราบค่าให้ใส่ 'c' ด้วยค่าดัมมี่เมื่อคุณสร้างคำสั่ง

d1 = OrderedDict([('c', None), ('a', '1'), ('b', '2')])

และเปลี่ยนค่าในภายหลัง

d1['c'] = 3

มีวิธีแทรกลงในคำสั่งเพื่อให้องค์ประกอบยังคงเรียงอยู่ (เพิ่มขึ้น / ลดลง)
Eswar

คำสั่งตามคำสั่งจะไม่จัดเรียงตามคุณสมบัติของรายการใด ๆ เรียงตามลำดับการแทรกซึ่งไม่มีส่วนเกี่ยวข้องกับรายการนั้น ๆ ใน CPthon 3.6 และใน Python 3.7 คำสั่งทั้งหมดจะถูกเรียงลำดับดังนั้นจึงมีเหตุผลเพียงเล็กน้อยที่จะใช้ OrderDict
Terry Jan Reedy


3

FWIW นี่คือโค้ดด่วน n-dirty ที่ฉันเขียนเพื่อแทรกไปยังตำแหน่งดัชนีโดยพลการ ไม่จำเป็นต้องมีประสิทธิภาพ แต่ใช้งานได้ในสถานที่

class OrderedDictInsert(OrderedDict):
    def insert(self, index, key, value):
        self[key] = value
        for ii, k in enumerate(list(self.keys())):
            if ii >= index and k != key:
                self.move_to_end(k)

3

คุณอาจต้องการที่จะใช้โครงสร้างที่แตกต่างกัน แต่มีวิธีที่จะทำมันในหลาม 2.7

d1 = OrderedDict([('a', '1'), ('b', '2')])
d2 = OrderedDict(c='3')
d2.update(d1)

d2 จะมี

>>> d2
OrderedDict([('c', '3'), ('a', '1'), ('b', '2')])

ดังที่ผู้อื่นกล่าวไว้ในpython 3.2คุณสามารถใช้OrderedDict.move_to_end('c', last=False)เพื่อย้ายคีย์ที่กำหนดหลังจากการแทรก

หมายเหตุ:พิจารณาว่าตัวเลือกแรกทำงานช้ากว่าสำหรับชุดข้อมูลขนาดใหญ่เนื่องจากการสร้าง OrderDict ใหม่และการคัดลอกค่าเก่า


2

หากคุณต้องการฟังก์ชั่นที่ไม่มีอยู่ให้ขยายชั้นเรียนด้วยสิ่งที่คุณต้องการ

from collections import OrderedDict

class OrderedDictWithPrepend(OrderedDict):
    def prepend(self, other):
        ins = []
        if hasattr(other, 'viewitems'):
            other = other.viewitems()
        for key, val in other:
            if key in self:
                self[key] = val
            else:
                ins.append((key, val))
        if ins:
            items = self.items()
            self.clear()
            self.update(ins)
            self.update(items)

ไม่ค่อยมีประสิทธิภาพ แต่ใช้งานได้:

o = OrderedDictWithPrepend()

o['a'] = 1
o['b'] = 2
print o
# OrderedDictWithPrepend([('a', 1), ('b', 2)])

o.prepend({'c': 3})
print o
# OrderedDictWithPrepend([('c', 3), ('a', 1), ('b', 2)])

o.prepend([('a',11),('d',55),('e',66)])
print o
# OrderedDictWithPrepend([('d', 55), ('e', 66), ('c', 3), ('a', 11), ('b', 2)])

1

ฉันขอแนะนำให้เพิ่มprepend()วิธีการในสูตร Python ActiveState ที่บริสุทธิ์นี้หรือรับคลาสย่อยจากมัน รหัสที่ต้องทำอาจมีประสิทธิภาพพอสมควรเนื่องจากโครงสร้างข้อมูลพื้นฐานสำหรับการสั่งซื้อเป็นรายการที่เชื่อมโยงกัน

อัปเดต

เพื่อพิสูจน์ว่าแนวทางนี้เป็นไปได้ด้านล่างนี้คือรหัสที่ทำตามสิ่งที่แนะนำ เพื่อเป็นโบนัสฉันได้ทำการเปลี่ยนแปลงเล็กน้อยเพิ่มเติมเพื่อให้ทำงานได้ทั้งใน Python 2.7.15 และ 3.7.1

prepend()วิธีการได้รับการเพิ่มชั้นในสูตรและได้รับการดำเนินการในแง่ของวิธีอื่นที่ได้รับการเพิ่มชื่อmove_to_end()ที่ถูกเพิ่มเข้ามาOrderedDictในหลาม 3.2

prepend()ยังสามารถนำไปใช้งานได้โดยตรงเกือบจะตรงตามที่แสดงไว้ตอนต้นของคำตอบของ @Ashwini Chaudhary และการทำเช่นนั้นอาจส่งผลให้เร็วขึ้นเล็กน้อย แต่ก็ถูกปล่อยให้เป็นแบบฝึกหัดสำหรับผู้อ่านที่มีแรงจูงใจ ...

# Ordered Dictionary for Py2.4 from https://code.activestate.com/recipes/576693

# Backport of OrderedDict() class that runs on Python 2.4, 2.5, 2.6, 2.7 and pypy.
# Passes Python2.7's test suite and incorporates all the latest updates.

try:
    from thread import get_ident as _get_ident
except ImportError:  # Python 3
#    from dummy_thread import get_ident as _get_ident
    from _thread import get_ident as _get_ident  # Changed - martineau

try:
    from _abcoll import KeysView, ValuesView, ItemsView
except ImportError:
    pass

class MyOrderedDict(dict):
    'Dictionary that remembers insertion order'
    # An inherited dict maps keys to values.
    # The inherited dict provides __getitem__, __len__, __contains__, and get.
    # The remaining methods are order-aware.
    # Big-O running times for all methods are the same as for regular dictionaries.

    # The internal self.__map dictionary maps keys to links in a doubly linked list.
    # The circular doubly linked list starts and ends with a sentinel element.
    # The sentinel element never gets deleted (this simplifies the algorithm).
    # Each link is stored as a list of length three:  [PREV, NEXT, KEY].

    def __init__(self, *args, **kwds):
        '''Initialize an ordered dictionary.  Signature is the same as for
        regular dictionaries, but keyword arguments are not recommended
        because their insertion order is arbitrary.

        '''
        if len(args) > 1:
            raise TypeError('expected at most 1 arguments, got %d' % len(args))
        try:
            self.__root
        except AttributeError:
            self.__root = root = []  # sentinel node
            root[:] = [root, root, None]
            self.__map = {}
        self.__update(*args, **kwds)

    def prepend(self, key, value):  # Added to recipe.
        self.update({key: value})
        self.move_to_end(key, last=False)

    #### Derived from cpython 3.2 source code.
    def move_to_end(self, key, last=True):  # Added to recipe.
        '''Move an existing element to the end (or beginning if last==False).

        Raises KeyError if the element does not exist.
        When last=True, acts like a fast version of self[key]=self.pop(key).
        '''
        PREV, NEXT, KEY = 0, 1, 2

        link = self.__map[key]
        link_prev = link[PREV]
        link_next = link[NEXT]
        link_prev[NEXT] = link_next
        link_next[PREV] = link_prev
        root = self.__root

        if last:
            last = root[PREV]
            link[PREV] = last
            link[NEXT] = root
            last[NEXT] = root[PREV] = link
        else:
            first = root[NEXT]
            link[PREV] = root
            link[NEXT] = first
            root[NEXT] = first[PREV] = link
    ####

    def __setitem__(self, key, value, dict_setitem=dict.__setitem__):
        'od.__setitem__(i, y) <==> od[i]=y'
        # Setting a new item creates a new link which goes at the end of the linked
        # list, and the inherited dictionary is updated with the new key/value pair.
        if key not in self:
            root = self.__root
            last = root[0]
            last[1] = root[0] = self.__map[key] = [last, root, key]
        dict_setitem(self, key, value)

    def __delitem__(self, key, dict_delitem=dict.__delitem__):
        'od.__delitem__(y) <==> del od[y]'
        # Deleting an existing item uses self.__map to find the link which is
        # then removed by updating the links in the predecessor and successor nodes.
        dict_delitem(self, key)
        link_prev, link_next, key = self.__map.pop(key)
        link_prev[1] = link_next
        link_next[0] = link_prev

    def __iter__(self):
        'od.__iter__() <==> iter(od)'
        root = self.__root
        curr = root[1]
        while curr is not root:
            yield curr[2]
            curr = curr[1]

    def __reversed__(self):
        'od.__reversed__() <==> reversed(od)'
        root = self.__root
        curr = root[0]
        while curr is not root:
            yield curr[2]
            curr = curr[0]

    def clear(self):
        'od.clear() -> None.  Remove all items from od.'
        try:
            for node in self.__map.itervalues():
                del node[:]
            root = self.__root
            root[:] = [root, root, None]
            self.__map.clear()
        except AttributeError:
            pass
        dict.clear(self)

    def popitem(self, last=True):
        '''od.popitem() -> (k, v), return and remove a (key, value) pair.
        Pairs are returned in LIFO order if last is true or FIFO order if false.

        '''
        if not self:
            raise KeyError('dictionary is empty')
        root = self.__root
        if last:
            link = root[0]
            link_prev = link[0]
            link_prev[1] = root
            root[0] = link_prev
        else:
            link = root[1]
            link_next = link[1]
            root[1] = link_next
            link_next[0] = root
        key = link[2]
        del self.__map[key]
        value = dict.pop(self, key)
        return key, value

    # -- the following methods do not depend on the internal structure --

    def keys(self):
        'od.keys() -> list of keys in od'
        return list(self)

    def values(self):
        'od.values() -> list of values in od'
        return [self[key] for key in self]

    def items(self):
        'od.items() -> list of (key, value) pairs in od'
        return [(key, self[key]) for key in self]

    def iterkeys(self):
        'od.iterkeys() -> an iterator over the keys in od'
        return iter(self)

    def itervalues(self):
        'od.itervalues -> an iterator over the values in od'
        for k in self:
            yield self[k]

    def iteritems(self):
        'od.iteritems -> an iterator over the (key, value) items in od'
        for k in self:
            yield (k, self[k])

    def update(*args, **kwds):
        '''od.update(E, **F) -> None.  Update od from dict/iterable E and F.

        If E is a dict instance, does:           for k in E: od[k] = E[k]
        If E has a .keys() method, does:         for k in E.keys(): od[k] = E[k]
        Or if E is an iterable of items, does:   for k, v in E: od[k] = v
        In either case, this is followed by:     for k, v in F.items(): od[k] = v

        '''
        if len(args) > 2:
            raise TypeError('update() takes at most 2 positional '
                            'arguments (%d given)' % (len(args),))
        elif not args:
            raise TypeError('update() takes at least 1 argument (0 given)')
        self = args[0]
        # Make progressively weaker assumptions about "other"
        other = ()
        if len(args) == 2:
            other = args[1]
        if isinstance(other, dict):
            for key in other:
                self[key] = other[key]
        elif hasattr(other, 'keys'):
            for key in other.keys():
                self[key] = other[key]
        else:
            for key, value in other:
                self[key] = value
        for key, value in kwds.items():
            self[key] = value

    __update = update  # let subclasses override update without breaking __init__

    __marker = object()

    def pop(self, key, default=__marker):
        '''od.pop(k[,d]) -> v, remove specified key and return the corresponding value.
        If key is not found, d is returned if given, otherwise KeyError is raised.

        '''
        if key in self:
            result = self[key]
            del self[key]
            return result
        if default is self.__marker:
            raise KeyError(key)
        return default

    def setdefault(self, key, default=None):
        'od.setdefault(k[,d]) -> od.get(k,d), also set od[k]=d if k not in od'
        if key in self:
            return self[key]
        self[key] = default
        return default

    def __repr__(self, _repr_running={}):
        'od.__repr__() <==> repr(od)'
        call_key = id(self), _get_ident()
        if call_key in _repr_running:
            return '...'
        _repr_running[call_key] = 1
        try:
            if not self:
                return '%s()' % (self.__class__.__name__,)
            return '%s(%r)' % (self.__class__.__name__, self.items())
        finally:
            del _repr_running[call_key]

    def __reduce__(self):
        'Return state information for pickling'
        items = [[k, self[k]] for k in self]
        inst_dict = vars(self).copy()
        for k in vars(MyOrderedDict()):
            inst_dict.pop(k, None)
        if inst_dict:
            return (self.__class__, (items,), inst_dict)
        return self.__class__, (items,)

    def copy(self):
        'od.copy() -> a shallow copy of od'
        return self.__class__(self)

    @classmethod
    def fromkeys(cls, iterable, value=None):
        '''OD.fromkeys(S[, v]) -> New ordered dictionary with keys from S
        and values equal to v (which defaults to None).

        '''
        d = cls()
        for key in iterable:
            d[key] = value
        return d

    def __eq__(self, other):
        '''od.__eq__(y) <==> od==y.  Comparison to another OD is order-sensitive
        while comparison to a regular mapping is order-insensitive.

        '''
        if isinstance(other, MyOrderedDict):
            return len(self)==len(other) and self.items() == other.items()
        return dict.__eq__(self, other)

    def __ne__(self, other):
        return not self == other

    # -- the following methods are only used in Python 2.7 --

    def viewkeys(self):
        "od.viewkeys() -> a set-like object providing a view on od's keys"
        return KeysView(self)

    def viewvalues(self):
        "od.viewvalues() -> an object providing a view on od's values"
        return ValuesView(self)

    def viewitems(self):
        "od.viewitems() -> a set-like object providing a view on od's items"
        return ItemsView(self)

if __name__ == '__main__':

    d1 = MyOrderedDict([('a', '1'), ('b', '2')])
    d1.update({'c':'3'})
    print(d1)  # -> MyOrderedDict([('a', '1'), ('b', '2'), ('c', '3')])

    d2 = MyOrderedDict([('a', '1'), ('b', '2')])
    d2.prepend('c', 100)
    print(d2)  # -> MyOrderedDict([('c', 100), ('a', '1'), ('b', '2')])


1

ผมได้วงอินฟินิตี้ในขณะที่พยายามที่จะพิมพ์หรือบันทึกพจนานุกรมใช้@Ashwini Chaudhary2.7คำตอบกับงูหลาม แต่ฉันสามารถลดรหัสของเขาลงเล็กน้อยและทำให้มันใช้งานได้ที่นี่:

def move_to_dict_beginning(dictionary, key):
    """
        Move a OrderedDict item to its beginning, or add it to its beginning.
        Compatible with Python 2.7
    """

    if sys.version_info[0] < 3:
        value = dictionary[key]
        del dictionary[key]
        root = dictionary._OrderedDict__root

        first = root[1]
        root[1] = first[0] = dictionary._OrderedDict__map[key] = [root, first, key]
        dict.__setitem__(dictionary, key, value)

    else:
        dictionary.move_to_end( key, last=False )

มันยอดเยี่ยมมาก! ทำงานให้ฉัน!
Souvik Ray

0

นี่เป็นค่าดีฟอลต์ตามคำสั่งซึ่งอนุญาตให้แทรกรายการในตำแหน่งใด ๆ และใช้ไฟล์. ตัวดำเนินการในการสร้างคีย์:

from collections import OrderedDict

class defdict(OrderedDict):

    _protected = ["_OrderedDict__root", "_OrderedDict__map", "_cb"]    
    _cb = None

    def __init__(self, cb=None):
        super(defdict, self).__init__()
        self._cb = cb

    def __setattr__(self, name, value):
        # if the attr is not in self._protected set a key
        if name in self._protected:
            OrderedDict.__setattr__(self, name, value)
        else:
            OrderedDict.__setitem__(self, name, value)

    def __getattr__(self, name):
        if name in self._protected:
            return OrderedDict.__getattr__(self, name)
        else:
            # implements missing keys
            # if there is a callable _cb, create a key with its value
            try:
                return OrderedDict.__getitem__(self, name)
            except KeyError as e:
                if callable(self._cb):
                    value = self[name] = self._cb()
                    return value
                raise e

    def insert(self, index, name, value):
        items = [(k, v) for k, v in self.items()]
        items.insert(index, (name, value))
        self.clear()
        for k, v in items:
            self[k] = v


asd = defdict(lambda: 10)
asd.k1 = "Hey"
asd.k3 = "Bye"
asd.k4 = "Hello"
asd.insert(1, "k2", "New item")
print asd.k5 # access a missing key will create one when there is a callback
# 10
asd.k6 += 5  # adding to a missing key
print asd.k6
# 15
print asd.keys()
# ['k1', 'k2', 'k3', 'k4', 'k5', 'k6']
print asd.values()
# ['Hey', 'New item', 'Bye', 'Hello', 10, 15]
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.