我有这个:
d1 = OrderedDict([('a', '1'), ('b', '2')])
如果我这样做:
d1.update({'c':'3'})
然后我明白了:
OrderedDict([('a', '1'), ('b', '2'), ('c', '3')])
但我想要这个:
[('c', '3'), ('a', '1'), ('b', '2')]
没有创建新词典。
答案 0 :(得分:59)
在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&gt; = 3.2) Python 3.2 introduced 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')])
答案 1 :(得分:14)
编辑(2019-02-03)
请注意,以下答案仅适用于旧版本的Python。最近,OrderedDict
已在C中重写。此外,这确实触及了双下划线属性,这是不赞成的。
我刚刚在我的一个项目中写了一个OrderedDict
的子类,用于类似的目的。 Here's the gist
与大多数解决方案不同,插入操作也是常量时间O(1)
(它们不需要您重建数据结构)。
>>> d1 = ListDict([('a', '1'), ('b', '2')])
>>> d1.insert_before('a', ('c', 3))
>>> d1
ListDict([('c', 3), ('a', '1'), ('b', '2')])
答案 2 :(得分:11)
您必须创建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 :(得分:7)
如果你知道你需要一个'c'键,但不知道该值,那么在创建dict时插入带有虚拟值的'c'。
d1 = OrderedDict([('c', None), ('a', '1'), ('b', '2')])
并稍后更改该值。
d1['c'] = 3
答案 4 :(得分:4)
现在可以使用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
答案 5 :(得分: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)])
答案 6 :(得分:2)
FWIW这是我为插入任意索引位置而编写的快速脏代码。不一定有效但它可以就地工作。
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)
答案 7 :(得分:2)
您可能希望完全使用不同的结构,但有很多方法可以在 python 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)
在插入后移动指定的密钥。
注意:考虑到由于创建了新的OrderedDict和复制旧值,大型数据集的第一个选项较慢。
答案 8 :(得分:1)
我建议在这个纯Python ActiveState recipe中添加prepend()
方法或从中派生一个子类。考虑到订购的基础数据结构是链表,这样做的代码可能是相当有效的。
为了证明这种方法是可行的,下面是执行建议的代码。作为奖励,我还进行了一些额外的微小改动,以便在Python 2.7.15和3.7.1中工作。
已在配方中的类中添加了prepend()
方法,并已根据已添加名为move_to_end()
的另一个方法实现,该方法已添加到Python 3.2中的OrderedDict
。
prepend()
也可以直接实现,几乎完全如@Ashwini Chaudhary的answer开头所示 - 并且这样做可能会导致它稍快一点,但这仍然是一个练习对于有动力的读者......
# 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')])
答案 9 :(得分:0)
在尝试使用@Ashwini Chaudhary和Python 2.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 )
答案 10 :(得分: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]