Aku punya ini:
d1 = OrderedDict([('a', '1'), ('b', '2')])
Jika saya melakukan ini:
d1.update({'c':'3'})
Lalu saya mendapatkan ini:
OrderedDict([('a', '1'), ('b', '2'), ('c', '3')])
tapi saya ingin ini:
[('c', '3'), ('a', '1'), ('b', '2')]
tanpa membuat kamus baru.
python
python-3.x
dictionary
python-2.x
ordereddict
pengguna2392209
sumber
sumber
Jawaban:
Tidak ada metode bawaan untuk melakukan ini dengan Python 2. Jika Anda membutuhkan ini, Anda perlu menulis
prepend()
metode / fungsi yang beroperasi padaOrderedDict
internal dengan kompleksitas O (1).Untuk Python 3.2 dan yang lebih baru, Anda harus menggunakan
move_to_end
metode ini. Metode menerimalast
argumen yang menunjukkan apakah elemen akan dipindahkan ke bawah (last=True
) atau atas (last=False
) dariOrderedDict
.Terakhir, jika Anda menginginkan solusi yang cepat, kotor, dan lambat , Anda dapat membuat yang baru
OrderedDict
dari awal.Detail untuk empat solusi berbeda:
Perluas
OrderedDict
dan tambahkan metode instance barufrom 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)
Demo:
>>> 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')])
Fungsi mandiri yang memanipulasi
OrderedDict
objekFungsi ini melakukan hal yang sama dengan menerima objek dikt, kunci dan nilai. Saya pribadi lebih suka kelas:
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)
Demo:
>>> 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')])
Gunakan
OrderedDict.move_to_end()
(Python> = 3.2)Python 3.2 diperkenalkan pada
OrderedDict.move_to_end()
metode. Dengan menggunakannya, kita dapat memindahkan kunci yang ada ke salah satu ujung kamus dalam waktu 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')])
Jika kita perlu memasukkan elemen dan memindahkannya ke atas, semua dalam satu langkah, kita bisa langsung menggunakannya untuk membuat
prepend()
pembungkus (tidak disajikan di sini).Buat baru
OrderedDict
- lambat !!!Jika Anda tidak ingin melakukan itu dan kinerja tidak menjadi masalah, maka cara termudah adalah dengan membuat dict baru:
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
keluaran:
OrderedDict([('c', 3), ('e', 5), ('a', '1'), ('b', '2')])
sumber
c
sudah ada, ini tidak akan memperbarui nilai lamamove_to_end
maka tidak ada tag Python 3 pada pertanyaan,move_to_end
hanya berfungsi di Python 3.2+. Saya akan memperbarui jawaban saya untuk memasukkan solusi berbasis Python 3. Terima kasih banyak atas pembaruannya!move_to_front
, mungkin lebih baik menerapkanmove_to_front
metode daripadaprepend
metode terpisah ? Ini akan membuat kode Anda lebih portabel jika Anda perlu mendukung Python 2 dan Python 3 dari basis kode yang sama.dict_setitem=dict.__setitem__
sebagai paramprepend
? Mengapa / haruskah seseorang mengoper setter yang berbeda?ordered_dict_prepend
atas. Memanggilordered_dict_prepend(d, 'c', 100)
dua kali dan mencoba mencetak dikt yang dihasilkan (hanya dengan masukd
ke konsol Python) hasil dalam proses Python terus mengambil memori. Diuji dengan Python 2.7.10EDIT (2019-02-03) Perhatikan bahwa jawaban berikut hanya berfungsi pada versi Python yang lebih lama. Baru-baru ini,
OrderedDict
telah ditulis ulang di C. Selain itu, ini menyentuh atribut garis bawah ganda yang tidak disukai.Saya baru saja menulis subclass dari
OrderedDict
proyek saya untuk tujuan yang sama. Inilah intinya .Operasi penyisipan juga merupakan waktu yang konstan
O(1)
(tidak mengharuskan Anda untuk membangun kembali struktur data), tidak seperti kebanyakan solusi ini.>>> d1 = ListDict([('a', '1'), ('b', '2')]) >>> d1.insert_before('a', ('c', 3)) >>> d1 ListDict([('c', 3), ('a', '1'), ('b', '2')])
sumber
TypeError: '_Link' object does not support indexing
saat menggunakan ini di Python 3.4.OrderedDict
telah ditulis ulang dalam C pada Python 3.5, dan subclass ini melakukan tabu mucking dengan internal (sebenarnya membalikkan nama mangling untuk mengakses __ properti).Anda harus membuat contoh baru dari
OrderedDict
. Jika kunci Anda unik: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)])
Tetapi jika tidak, waspadalah karena perilaku ini mungkin atau mungkin tidak diinginkan untuk Anda:
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)])
sumber
OrderedDict
tidak stabil?Jika Anda tahu Anda akan menginginkan kunci 'c', tetapi tidak tahu nilainya, masukkan 'c' dengan nilai dummy saat Anda membuat dict.
d1 = OrderedDict([('c', None), ('a', '1'), ('b', '2')])
dan ubah nilainya nanti.
d1['c'] = 3
sumber
Ini sekarang dimungkinkan dengan move_to_end (key, last = True)
>>> d = OrderedDict.fromkeys('abcde') >>> d.move_to_end('b') >>> ''.join(d.keys()) 'acdeb' >>> d.move_to_end('b', last=False) >>> ''.join(d.keys()) 'bacde'
https://docs.python.org/3/library/collections.html#collections.OrderedDict.move_to_end
sumber
FWIW Berikut adalah kode quick-n-dirty yang saya tulis untuk menyisipkan ke posisi indeks arbitrer. Belum tentu efisien, tetapi berfungsi di tempat.
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)
sumber
Anda mungkin ingin menggunakan struktur yang berbeda sama sekali, tetapi ada cara untuk melakukannya di python 2.7 .
d1 = OrderedDict([('a', '1'), ('b', '2')]) d2 = OrderedDict(c='3') d2.update(d1)
d2 kemudian akan berisi
>>> d2 OrderedDict([('c', '3'), ('a', '1'), ('b', '2')])
Seperti yang disebutkan oleh orang lain, di python 3.2 Anda dapat menggunakan
OrderedDict.move_to_end('c', last=False)
untuk memindahkan kunci tertentu setelah penyisipan.sumber
Jika Anda membutuhkan fungsionalitas yang tidak ada, cukup luaskan kelas dengan apa pun yang Anda inginkan:
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)
Tidak terlalu efisien, tetapi berfungsi:
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)])
sumber
Saya akan menyarankan untuk menambahkan
prepend()
metode ke resep Python ActiveState murni ini atau mengambil subclass darinya. Kode untuk melakukannya bisa menjadi cukup efisien mengingat bahwa struktur data yang mendasari untuk pemesanan adalah daftar-tertaut.Memperbarui
Untuk membuktikan bahwa pendekatan ini layak, di bawah ini adalah kode yang melakukan apa yang disarankan. Sebagai bonus, saya juga membuat beberapa perubahan kecil tambahan untuk mulai bekerja dengan Python 2.7.15 dan 3.7.1.
Sebuah
prepend()
metode telah ditambahkan ke kelas dalam resep dan telah diimplementasikan dalam istilah metode lain yang telah ditambahkan bernamamove_to_end()
, yang ditambahkan keOrderedDict
dalam Python 3.2.prepend()
juga dapat diimplementasikan secara langsung, hampir persis seperti yang ditunjukkan di awal jawaban @Ashwini Chaudhary —dan melakukan hal itu kemungkinan besar akan menghasilkannya sedikit lebih cepat, tetapi itu dibiarkan sebagai latihan bagi pembaca yang termotivasi ...# 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')])
sumber
Saya mendapat infinity loop ketika mencoba untuk mencetak atau menyimpan kamus menggunakan jawaban Chaudhary @Ashwini dengan Python
2.7
. Tetapi saya berhasil mengurangi kodenya sedikit, dan membuatnya berfungsi di sini: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 )
sumber
Ini adalah default, dikt memerintahkan yang memungkinkan untuk menyisipkan item di posisi apa pun dan menggunakan. operator untuk membuat kunci:
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]
sumber