更改字典中键的名称
- 2024-12-31 08:37:00
- admin 原创
- 112
问题描述:
如何更改 Python 字典中条目的键?
解决方案 1:
只需两步即可轻松完成:
dictionary[new_key] = dictionary[old_key]
del dictionary[old_key]
或者只需一步:
dictionary[new_key] = dictionary.pop(old_key)
KeyError
如果dictionary[old_key]
未定义,则将引发。请注意,这将删除dictionary[old_key]
。
>>> dictionary = { 1: 'one', 2:'two', 3:'three' }
>>> dictionary['ONE'] = dictionary.pop(1)
>>> dictionary
{2: 'two', 3: 'three', 'ONE': 'one'}
>>> dictionary['ONE'] = dictionary.pop(1)
Traceback (most recent call last):
File "<input>", line 1, in <module>
KeyError: 1
解决方案 2:
如果你想改变所有的键:
d = {'x':1, 'y':2, 'z':3}
d1 = {'x':'a', 'y':'b', 'z':'c'}
In [10]: dict((d1[key], value) for (key, value) in d.items())
Out[10]: {'a': 1, 'b': 2, 'c': 3}
如果您想更改单个键:您可以采用上述任何建议。
解决方案 3:
新鲜汽水
>>>a = {1:2, 3:4}
>>>a[5] = a.pop(1)
>>>a
{3: 4, 5: 2}
>>>
解决方案 4:
在 Python 2.7 及更高版本中,您可以使用字典理解:这是我在使用 DictReader 读取 CSV 时遇到的一个例子。用户在所有列名后加上了“:”
ori_dict = {'key1:' : 1, 'key2:' : 2, 'key3:' : 3}
去掉键结尾的‘:’:
corrected_dict = { k.replace(':', ''): v for k, v in ori_dict.items() }
解决方案 5:
d = {1:2,3:4}
假设我们要将键更改为列表元素 p=['a', 'b']。以下代码即可:
d=dict(zip(p,list(d.values())))
我们得到
{'a': 2, 'b': 4}
解决方案 6:
由于键是字典用来查找值的,因此您无法真正更改它们。您可以做的最接近的事情是保存与旧键关联的值,删除它,然后使用替换键和保存的值添加新条目。其他几个答案说明了可以实现此目的的不同方法。
解决方案 7:
您可以使用 iff/else 字典理解。此方法允许您在一行中替换任意数量的键,并且不需要您更改所有键。
key_map_dict = {'a':'apple','c':'cat'}
d = {'a':1,'b':2,'c':3}
d = {(key_map_dict[k] if k in key_map_dict else k):v for (k,v) in d.items() }
返回{'apple':1,'b':2,'cat':3}
解决方案 8:
没有直接的方法可以做到这一点,但你可以删除然后分配
d = {1:2,3:4}
d[newKey] = d[1]
del d[1]
或者进行批量密钥修改:
d = dict((changeKey(k), v) for k, v in d.items())
解决方案 9:
如果你有一个复杂的字典,则意味着字典中有一个字典或列表:
myDict = {1:"one",2:{3:"three",4:"four"}}
myDict[2][5] = myDict[2].pop(4)
print myDict
Output
{1: 'one', 2: {3: 'three', 5: 'four'}}
解决方案 10:
转换字典中的所有键
假设这是你的字典:
>>> sample = {'person-id': '3', 'person-name': 'Bob'}
要将示例字典键中的所有破折号转换为下划线:
>>> sample = {key.replace('-', '_'): sample.pop(key) for key in sample.keys()}
>>> sample
>>> {'person_id': '3', 'person_name': 'Bob'}
解决方案 11:
此函数获取一个字典,以及另一个指定如何重命名键的字典;它返回一个带有重命名键的新字典:
def rekey(inp_dict, keys_replace):
return {keys_replace.get(k, k): v for k, v in inp_dict.items()}
测试:
def test_rekey():
assert rekey({'a': 1, "b": 2, "c": 3}, {"b": "beta"}) == {'a': 1, "beta": 2, "c": 3}
解决方案 12:
假设需要一次性更改所有键。下面是我对这些键进行词干处理。
a = {'making' : 1, 'jumping' : 2, 'climbing' : 1, 'running' : 2}
b = {ps.stem(w) : a[w] for w in a.keys()}
print(b)
>>> {'climb': 1, 'jump': 2, 'make': 1, 'run': 2} #output
解决方案 13:
这将使所有字典键都变成小写。即使您有嵌套字典或列表。您可以执行类似操作来应用其他转换。
def lowercase_keys(obj):
if isinstance(obj, dict):
obj = {key.lower(): value for key, value in obj.items()}
for key, value in obj.items():
if isinstance(value, list):
for idx, item in enumerate(value):
value[idx] = lowercase_keys(item)
obj[key] = lowercase_keys(value)
return obj
json_str = {"FOO": "BAR", "BAR": 123, "EMB_LIST": [{"FOO": "bar", "Bar": 123}, {"FOO": "bar", "Bar": 123}], "EMB_DICT": {"FOO": "BAR", "BAR": 123, "EMB_LIST": [{"FOO": "bar", "Bar": 123}, {"FOO": "bar", "Bar": 123}]}}
lowercase_keys(json_str)
Out[0]: {'foo': 'BAR',
'bar': 123,
'emb_list': [{'foo': 'bar', 'bar': 123}, {'foo': 'bar', 'bar': 123}],
'emb_dict': {'foo': 'BAR',
'bar': 123,
'emb_list': [{'foo': 'bar', 'bar': 123}, {'foo': 'bar', 'bar': 123}]}}
解决方案 14:
用下划线替换字典键中的空格,我使用这个简单的方法......
for k in dictionary.copy():
if ' ' in k:
dictionary[ k.replace(' ', '_') ] = dictionary.pop(k, 'e r r')
或者只是 dictionary.pop(k)
注意“er r”(可以是任何字符串)将成为新值,如果该键不在字典中,则无法替换它,而这在这里是不可能发生的。该参数是可选的,在其他类似的代码中,可能会发生 KeyError,添加的参数可以避免这种情况,但仍然可以使用该“er r”或您将其设置为任何值的新键。
.copy() 避免...字典在迭代过程中改变大小。
.keys() 不需要,k 是每个键,在我看来 k 代表键。
(我使用的是 v3.7)
有关字典 pop() 的信息
上面循环的一行代码是什么?
解决方案 15:
注意弹出的位置:
将要删除的键放在 pop() 之后
orig_dict['AAAAA'] = orig_dict.pop('A')
orig_dict = {'A': 1, 'B' : 5, 'C' : 10, 'D' : 15}
# printing initial
print ("original: ", orig_dict)
# changing keys of dictionary
orig_dict['AAAAA'] = orig_dict.pop('A')
# printing final result
print ("Changed: ", str(orig_dict))
解决方案 16:
我只需要帮助我的妻子为 Python 课程做一些类似的事情,所以我编写了这段代码来向她展示如何做。正如标题所说,它只替换了一个键名。很少需要替换一个键名,并保持字典的顺序不变,但无论如何我还是想分享一下,因为这篇文章是 Goggle 搜索时返回的内容,尽管这是一个非常古老的帖子。
代码:
dictionary = {
"cat": "meow",
"dog": "woof",
"cow": "ding ding ding",
"goat": "beh"
}
def countKeys(dictionary):
num = 0
for key, value in dictionary.items():
num += 1
return num
def keyPosition(dictionary, search):
num = 0
for key, value in dictionary.items():
if key == search:
return num
num += 1
def replaceKey(dictionary, position, newKey):
num = 0
updatedDictionary = {}
for key, value in dictionary.items():
if num == position:
updatedDictionary.update({newKey: value})
else:
updatedDictionary.update({key: value})
num += 1
return updatedDictionary
for x in dictionary:
print("A", x, "goes", dictionary[x])
numKeys = countKeys(dictionary)
print("There are", numKeys, "animals in this list.
")
print("Woops, that's not what a cow says...")
keyPos = keyPosition(dictionary, "cow")
print("Cow is in the", keyPos, "position, lets put a fox there instead...
")
dictionary = replaceKey(dictionary, keyPos, "fox")
for x in dictionary:
print("A", x, "goes", dictionary[x])
输出:
A cat goes meow
A dog goes woof
A cow goes ding ding ding
A goat goes beh
There are 4 animals in this list.
Woops, that's not what a cow says...
Cow is in the 2 position, lets put a fox there instead...
A cat goes meow
A dog goes woof
A fox goes ding ding ding
A goat goes beh
解决方案 17:
您可以将相同的值与多个键关联,或者仅删除一个键并重新添加具有相同值的新键。
例如,如果您有键->值:
red->1
blue->2
green->4
没有理由你不能添加purple->2
或删除red->1
并添加orange->1
解决方案 18:
如果有人想替换多级字典中所有出现的键,则方法。
函数检查字典中是否有特定的键,然后遍历子字典并递归调用该函数:
def update_keys(old_key,new_key,d):
if isinstance(d,dict):
if old_key in d:
d[new_key] = d[old_key]
del d[old_key]
for key in d:
updateKey(old_key,new_key,d[key])
update_keys('old','new',dictionary)
解决方案 19:
完整解决方案示例
声明一个包含所需映射的 json 文件
{
"old_key_name": "new_key_name",
"old_key_name_2": "new_key_name_2",
}
加载
with open("<filepath>") as json_file:
format_dict = json.load(json_file)
创建此函数来使用你的映射来格式化字典
def format_output(dict_to_format,format_dict):
for row in dict_to_format:
if row in format_dict.keys() and row != format_dict[row]:
dict_to_format[format_dict[row]] = dict_to_format.pop(row)
return dict_to_format
解决方案 20:
我在下面写了这个函数,您可以在其中将当前键名更改为新键名。
def change_dictionary_key_name(dict_object, old_name, new_name):
'''
[PARAMETERS]:
dict_object (dict): The object of the dictionary to perform the change
old_name (string): The original name of the key to be changed
new_name (string): The new name of the key
[RETURNS]:
final_obj: The dictionary with the updated key names
Take the dictionary and convert its keys to a list.
Update the list with the new value and then convert the list of the new keys to
a new dictionary
'''
keys_list = list(dict_object.keys())
for i in range(len(keys_list)):
if (keys_list[i] == old_name):
keys_list[i] = new_name
final_obj = dict(zip(keys_list, list(dict_object.values())))
return final_obj
假设一个 JSON,您可以通过以下行调用它并重命名它:
data = json.load(json_file)
for item in data:
item = change_dictionary_key_name(item, old_key_name, new_key_name)
此处可以找到从列表到字典键的转换:
https://www.geeksforgeeks.org/python-ways-to-change-keys-in-dictionary/
解决方案 21:
有了熊猫,你就可以拥有这样的东西,
from pandas import DataFrame
df = DataFrame([{"fruit":"apple", "colour":"red"}])
df.rename(columns = {'fruit':'fruit_name'}, inplace = True)
df.to_dict('records')[0]
>>> {'fruit_name': 'apple', 'colour': 'red'}
解决方案 22:
我将密钥存储在列表中(col_names)
使用此列表我更新了 data_dict 中存在的所有键
data_dict = { key:value for key, value in zip(col_names,list(data_dict.values())) }
解决方案 23:
替换字典中某些键的名称。保持字典中键、值对的原始顺序,而不创建新字典:
在此示例中,我们替换字典 d 中三个键中的两个键的名称。我们希望将键名称“x”替换为“a”,将键名称“y”替换为“b”。我们希望保持字典的原始顺序,而不创建新字典。
注意:我们无法迭代字典 d,因为我们正在对字典 d 进行更改,因此我们将迭代 d.copy()
单向:获取键、值。删除键、值对。添加新的键、值对。值保持不变(无变化)。
# Original dictionary:
d = {'x': 1, 'y': 2, 'z': 3}
# Conversion dictionary:
d1 = {'x': 'a', 'y': 'b'}
# Run through all the key,value pairs in copy of the
# dictionary d.
for key,value in d.copy().items():
if key in d1.keys():
# We get the name of the new_key value from the dictionary d1 with key.
new_key = d1[key]
else:
# No change to the name of the key
new_key = key
# If there is no change to the name of the key, we must
# delete the old key,value pair before creating
# the new key,value pair.
del d[key]
# The new key,value pair goes to the end of the dictionary.
d[new_key] = value
输出:d = {'a': 1, 'b': 2, 'z': 3}
另一种方法:使用dictionary.pop(key)创建新的键值对并删除旧的键值对。
d = {'x': 1, 'y': 2, 'z': 3}
d1 = {'x': 'a', 'y': 'b'}
for key in d.copy().keys():
if key in d1.keys():
new_key = d1[key]
d[new_key] = d.pop(key)
else:
# We need to do this if we want to keep
#the order of the dictionary.
d[key] = d.pop(key)
输出:d = {'a': 1, 'b': 2, 'z': 3}
解决方案 24:
为了有效地保持顺序(另一种情况很简单,删除旧的并添加新的),避免有序字典需要重建(至少是部分重建),我整理了一个类(OrderedDictX),它扩展了 OrderedDict,并允许您高效地进行键更改,即复杂度为 O(1)。该实现也可以针对现在有序的内置字典类进行调整。
它使用 2 个额外的字典将更改的键(“外部” - 即它们对用户外部显示)重新映射到底层 OrderedDict 中的键(“内部”) - 字典将只保存已更改的键,因此只要没有进行任何键更改,它们就会为空。
性能测量:
import timeit
import random
# Efficiency tests
from collections import MutableMapping
class OrderedDictRaymond(dict, MutableMapping):
def __init__(self, *args, **kwds):
if len(args) > 1:
raise TypeError('expected at 1 argument, got %d', len(args))
if not hasattr(self, '_keys'):
self._keys = []
self.update(*args, **kwds)
def rename(self,key,new_key):
ind = self._keys.index(key) #get the index of old key, O(N) operation
self._keys[ind] = new_key #replace old key with new key in self._keys
self[new_key] = self[key] #add the new key, this is added at the end of self._keys
self._keys.pop(-1) #pop the last item in self._keys
dict.__delitem__(self, key)
def clear(self):
del self._keys[:]
dict.clear(self)
def __setitem__(self, key, value):
if key not in self:
self._keys.append(key)
dict.__setitem__(self, key, value)
def __delitem__(self, key):
dict.__delitem__(self, key)
self._keys.remove(key)
def __iter__(self):
return iter(self._keys)
def __reversed__(self):
return reversed(self._keys)
def popitem(self):
if not self:
raise KeyError
key = self._keys.pop()
value = dict.pop(self, key)
return key, value
def __reduce__(self):
items = [[k, self[k]] for k in self]
inst_dict = vars(self).copy()
inst_dict.pop('_keys', None)
return (self.__class__, (items,), inst_dict)
setdefault = MutableMapping.setdefault
update = MutableMapping.update
pop = MutableMapping.pop
keys = MutableMapping.keys
values = MutableMapping.values
items = MutableMapping.items
def __repr__(self):
pairs = ', '.join(map('%r: %r'.__mod__, self.items()))
return '%s({%s})' % (self.__class__.__name__, pairs)
def copy(self):
return self.__class__(self)
@classmethod
def fromkeys(cls, iterable, value=None):
d = cls()
for key in iterable:
d[key] = value
return d
class obj_container:
def __init__(self, obj) -> None:
self.obj = obj
def change_key_splice(container, k_old, k_new):
od = container.obj
container.obj = OrderedDict((k_new if k == k_old else k, v) for k, v in od.items())
def change_key_raymond(container, k_old, k_new):
od = container.obj
od.rename(k_old, k_new)
def change_key_odx(container, k_old, k_new):
odx = container.obj
odx.change_key(k_old, k_new)
NUM_ITEMS = 20000
od_splice = OrderedDict([(x, x) for x in range(NUM_ITEMS)])
od_raymond = OrderedDictRaymond(od_splice.items())
odx = OrderedDictX(od_splice.items())
od_splice, od_raymond, odx = [obj_container(d) for d in [od_splice, od_raymond, odx]]
assert odx.obj == od_splice.obj
assert odx.obj == od_raymond.obj
# Pick randomly half of the keys to change
keys_to_change = random.sample(range(NUM_ITEMS), NUM_ITEMS//2)
print(f'OrderedDictX: {timeit.timeit(lambda: [change_key_odx(odx, k, k+NUM_ITEMS) for k in keys_to_change], number=1)}')
print(f'OrderedDictRaymond: {timeit.timeit(lambda: [change_key_raymond(od_raymond, k, k+NUM_ITEMS) for k in keys_to_change], number=1)}')
print(f'Splice: {timeit.timeit(lambda: [change_key_splice(od_splice, k, k+NUM_ITEMS) for k in keys_to_change], number=1)}')
assert odx.obj == od_splice.obj
assert odx.obj == od_raymond.obj
结果:
OrderedDictX: 0.06587849999999995
OrderedDictRaymond: 1.1131364
Splice: 1165.2614647
正如预期的那样,拼接方法非常慢(虽然也没有想到会慢那么多)并且占用大量内存,而 O(N)Raymond 解决方案也更慢,在这个例子中是17倍。
当然,这个解决方案是 O(1),与 O(N)OrderedDictRaymond相比,随着字典大小的增加,时间差异变得更加明显,例如,对于 5 倍以上的元素(100000),O(N)现在慢了100 倍:
NUM_ITEMS = 100000
OrderedDictX: 0.3636919999999999
OrderedDictRaymond: 36.3963971
这是代码,如果您发现问题或提出改进建议,请发表评论,因为这仍然可能容易出错。
from collections import OrderedDict
class OrderedDictX(OrderedDict):
def __init__(self, *args, **kwargs):
# Mappings from new->old (ext2int), old->new (int2ext).
# Only the keys that are changed (internal key doesn't match what the user sees) are contained.
self._keys_ext2int = {}
self._keys_int2ext = {}
self.update(*args, **kwargs)
def change_key(self, k_old, k_new):
# Validate that the old key is part of the dict
if not self.__contains__(k_old):
raise Exception(f'Cannot rename key {k_old} to {k_new}: {k_old} not existing in dict')
# Return if no changing is actually to be done
if len(dict.fromkeys([k_old, k_new])) == 1:
return
# Validate that the new key would not conflict with another one
if self.__contains__(k_new):
raise Exception(f'Cannot rename key {k_old} to {k_new}: {k_new} already in dict')
# Preprocessing if key was already changed before
if k_old in self._keys_ext2int:
# Revert old change temporarily
k_old_int = self._keys_ext2int[k_old]
del self._keys_ext2int[k_old]
k_old = k_old_int
# Completely revert key-change and return if new key matches the internal key
if len(dict.fromkeys([k_old, k_new])) == 1:
del self._keys_int2ext[k_old]
return
# Finalize key change
self._keys_ext2int[k_new] = k_old
self._keys_int2ext[k_old] = k_new
def change_keys_multiple(self, rename_map: dict):
# For chaining changes: k1->k2, k2->k3, ..., kn->k_last
# we change backwards when k_last is not present;
# when k_last is k1 instead (circularity) we do almost the same
# except that we first change kn to a non-present key k_non_p,
# then after all the changes we can do the final change k_non_p->k1
already_changed, k_non_p = {}, None
for k_old, k_new in rename_map.items():
# If key was already changed due to chaining changes, we may advance
if k_old in already_changed:
continue
# If change is not chaining, directly change it
if k_new not in self:
self.change_key(k_old, k_new)
else:
# Change is chaining, find chain of changes
change_chain, is_circular = OrderedDict([(k_old, k_new)]), False
while True:
k_tail = next(iter(reversed(change_chain.values())))
# Chain ends if the tail key is no longer present or we detect circularity
if k_tail not in self:
break
if k_tail == k_old:
is_circular = True
break
# Continue chain
k_tail_new = rename_map[k_tail]
change_chain[k_tail] = k_tail_new
# Check for chain validity
if k_tail_new != k_old and k_tail_new in change_chain:
change_chain_str = ', '.join([f'{k1}->{k2}' for k1, k2 in change_chain.items()])
raise Exception(f'Invalid key-change chain detected: {change_chain_str}')
# NOP change (k->k) if length of chain is 1
if len(change_chain) == 1:
continue
# Circularity trick: first we change the last key to a generated non-present one
if is_circular:
if k_non_p is None:
k_non_p = self._get_non_present_key()
change_chain[next(iter(reversed(change_chain)))] = k_non_p
# Apply change chain in reverse
for k_old_chain, k_new_chain in reversed(change_chain.items()):
self.change_key(k_old_chain, k_new_chain)
already_changed[k_old_chain] = None
# Circularity trick: do the final rename
if is_circular:
self.change_key(k_non_p, k_old)
def _get_non_present_key(self):
i = len(self)
while i in self:
i -= 1
return i
def __contains__(self, k):
return (super().__contains__(k) and k not in self._keys_int2ext) or k in self._keys_ext2int
def __getitem__(self, k):
if not self.__contains__(k):
# Intentionally raise KeyError in ext2int
return self._keys_ext2int[k]
return super().__getitem__(self._keys_ext2int.get(k, k))
def __setitem__(self, k, v):
if k in self._keys_ext2int:
return super().__setitem__(self._keys_ext2int[k], v)
# If the key exists in the internal state but was renamed to a k_ext,
# employ this trick: make it such that it appears as if k_ext has also been renamed to k
if k in self._keys_int2ext:
k_ext = self._keys_int2ext[k]
self._keys_ext2int[k] = k_ext
k = k_ext
return super().__setitem__(k, v)
def __delitem__(self, k):
if not self.__contains__(k):
# Intentionally raise KeyError in ext2int
del self._keys_ext2int[k]
if k in self._keys_ext2int:
k_int = self._keys_ext2int[k]
del self._keys_ext2int[k]
del self._keys_int2ext[k_int]
k = k_int
return super().__delitem__(k)
def __iter__(self):
return self.keys()
def __reversed__(self):
for k in reversed(super().keys()):
yield self._keys_int2ext.get(k, k)
def __eq__(self, other: object) -> bool:
if not isinstance(other, dict):
return False
if len(self) != len(other):
return False
for (k, v), (k_other, v_other) in zip(self.items(), other.items()):
if k != k_other or v != v_other:
return False
return True
def get(self, k, default=None):
return self.__getitem__(k) if self.__contains__(k) else default
def popitem(self, last=True) -> tuple:
if not last:
k = next(iter(super().keys()))
else:
k = next(reversed(super().keys()))
v = super().__getitem__(k)
k = self._keys_int2ext.get(k, k)
self.__delitem__(k)
return k, v
class OrderedDictXKeysView:
def __init__(self, odx: 'OrderedDictX', orig_keys):
self._int2ext = odx._keys_int2ext
self._it = iter(orig_keys)
self._it_rev = reversed(orig_keys)
def __iter__(self):
return self
def __next__(self):
k = self._it.__next__()
return self._int2ext.get(k, k)
def __reversed__(self):
for k in self._it_rev:
yield self._int2ext.get(k, k)
class OrderedDictXItemsView:
def __init__(self, odx: 'OrderedDictX', orig_items):
self._int2ext = odx._keys_int2ext
self._it = iter(orig_items)
self._it_rev = reversed(orig_items)
def __iter__(self):
return self
def __next__(self):
k, v = self._it.__next__()
return self._int2ext.get(k, k), v
def __reversed__(self):
for k, v in self._it_rev:
yield self._int2ext.get(k, k), v
def keys(self):
return self.OrderedDictXKeysView(self, super().keys())
def items(self):
return self.OrderedDictXItemsView(self, super().items())
def copy(self):
return OrderedDictX(self.items())
# FIXME: move this to pytest
if __name__ == '__main__':
MAX = 25
items = [(i+1, i+1) for i in range(MAX)]
keys = [i[0] for i in items]
d = OrderedDictX(items)
# keys() before change
print(list(d.items()))
assert list(d.keys()) == keys
# __contains__ before change
assert 1 in d
# __getitem__ before change
assert d[1] == 1
# __setitem__ before change
d[1] = 100
assert d[1] == 100
d[1] = 1
assert d[1] == 1
# __delitem__ before change
assert MAX in d
del d[MAX]
assert MAX not in d
d[MAX] = MAX
assert MAX in d
print('== Tests before key change finished ==')
# change_key and __contains__
assert MAX-1 in d
assert MAX*2 not in d
d.change_key(MAX-1, MAX*2)
assert MAX-1 not in d
assert MAX*2 in d
# items() and keys()
items[MAX-2] = (MAX*2, MAX-1)
keys[MAX-2] = MAX*2
assert list(d.items()) == items
assert list(d.keys()) == keys
print(list(d.items()))
# __getitem__
assert d[MAX*2] == MAX-1
# __setitem__
d[MAX*2] = MAX*3
items[MAX-2] = (MAX*2, MAX*3)
keys[MAX-2] = MAX*2
assert list(d.items()) == items
assert list(d.keys()) == keys
# __delitem__
del d[MAX]
items = items[:-1]
keys = keys[:-1]
assert list(d.items()) == items
assert list(d.keys()) == keys
d[MAX] = MAX
items.append((MAX, MAX))
keys.append(MAX)
# __iter__
assert list(d) == keys
# __reversed__
print(list(reversed(d.items())))
assert list(reversed(d)) == list(reversed(keys))
assert list(reversed(d.keys())) == list(reversed(keys))
assert list(reversed(d.items())) == list(reversed(items))
# pop_item()
assert d.popitem() == (MAX, MAX)
assert d.popitem() == (MAX*2, MAX*3)
items = items[:-2]
keys = keys[:-2]
assert list(d.items()) == items
assert list(d.keys()) == keys
# update()
d.update({1: 1000, MAX-2: MAX*4})
items[0] = (1, 1000)
items[MAX-3] = (MAX-2, MAX*4)
assert list(d.items()) == items
assert list(d.keys()) == keys
# move_to_end()
d.move_to_end(1)
items = items[1:] + [items[0]]
keys = keys[1:] + [keys[0]]
assert list(d.items()) == items
assert list(d.keys()) == keys
assert list(d) == keys
# __eq__
d.change_key(1, 2000)
other_d = OrderedDictX(d.items())
assert d == other_d
assert other_d == d
import timeit
import random
# Efficiency tests
from collections import MutableMapping
class OrderedDictRaymond(dict, MutableMapping):
def __init__(self, *args, **kwds):
if len(args) > 1:
raise TypeError('expected at 1 argument, got %d', len(args))
if not hasattr(self, '_keys'):
self._keys = []
self.update(*args, **kwds)
def rename(self,key,new_key):
ind = self._keys.index(key) #get the index of old key, O(N) operation
self._keys[ind] = new_key #replace old key with new key in self._keys
self[new_key] = self[key] #add the new key, this is added at the end of self._keys
self._keys.pop(-1) #pop the last item in self._keys
dict.__delitem__(self, key)
def clear(self):
del self._keys[:]
dict.clear(self)
def __setitem__(self, key, value):
if key not in self:
self._keys.append(key)
dict.__setitem__(self, key, value)
def __delitem__(self, key):
dict.__delitem__(self, key)
self._keys.remove(key)
def __iter__(self):
return iter(self._keys)
def __reversed__(self):
return reversed(self._keys)
def popitem(self):
if not self:
raise KeyError
key = self._keys.pop()
value = dict.pop(self, key)
return key, value
def __reduce__(self):
items = [[k, self[k]] for k in self]
inst_dict = vars(self).copy()
inst_dict.pop('_keys', None)
return (self.__class__, (items,), inst_dict)
setdefault = MutableMapping.setdefault
update = MutableMapping.update
pop = MutableMapping.pop
keys = MutableMapping.keys
values = MutableMapping.values
items = MutableMapping.items
def __repr__(self):
pairs = ', '.join(map('%r: %r'.__mod__, self.items()))
return '%s({%s})' % (self.__class__.__name__, pairs)
def copy(self):
return self.__class__(self)
@classmethod
def fromkeys(cls, iterable, value=None):
d = cls()
for key in iterable:
d[key] = value
return d
class obj_container:
def __init__(self, obj) -> None:
self.obj = obj
def change_key_splice(container, k_old, k_new):
od = container.obj
container.obj = OrderedDict((k_new if k == k_old else k, v) for k, v in od.items())
def change_key_raymond(container, k_old, k_new):
od = container.obj
od.rename(k_old, k_new)
def change_key_odx(container, k_old, k_new):
odx = container.obj
odx.change_key(k_old, k_new)
NUM_ITEMS = 100000
od_splice = OrderedDict([(x, x) for x in range(NUM_ITEMS)])
od_raymond = OrderedDictRaymond(od_splice.items())
odx = OrderedDictX(od_splice.items())
od_splice, od_raymond, odx = [obj_container(d) for d in [od_splice, od_raymond, odx]]
assert odx.obj == od_splice.obj
assert odx.obj == od_raymond.obj
# Pick randomly half of the keys to change
keys_to_change = random.sample(range(NUM_ITEMS), NUM_ITEMS//2)
print(f'OrderedDictX: {timeit.timeit(lambda: [change_key_odx(odx, k, k+NUM_ITEMS) for k in keys_to_change], number=1)}')
#print(f'OrderedDictRaymond: {timeit.timeit(lambda: [change_key_raymond(od_raymond, k, k+NUM_ITEMS) for k in keys_to_change], number=1)}')
#print(f'Splice: {timeit.timeit(lambda: [change_key_splice(od_splice, k, k+NUM_ITEMS) for k in keys_to_change], number=1)}')
# assert odx.obj == od_splice.obj
# assert odx.obj == od_raymond.obj
keys_odx = list(odx.obj)
keys_raymond = list(od_raymond.obj)
keys_splice = list(od_splice.obj)
print('== items() ==')
print(f'OrderedDictX: {timeit.timeit(lambda: [(k, v) for k, v in odx.obj.items()], number=1)}')
print(f'OrderedDictRaymond: {timeit.timeit(lambda: [(k, v) for k, v in od_raymond.obj.items()], number=1)}')
print(f'OrderedDict: {timeit.timeit(lambda: [(k, v) for k, v in od_splice.obj.items()], number=1)}')
print('== keys() ==')
print(f'OrderedDictX: {timeit.timeit(lambda: [k for k in odx.obj.keys()], number=1)}')
print(f'OrderedDictRaymond: {timeit.timeit(lambda: [k for k in od_raymond.obj.keys()], number=1)}')
print(f'OrderedDict: {timeit.timeit(lambda: [k for k in od_splice.obj.keys()], number=1)}')
print('== list() ==')
print(f'OrderedDictX: {timeit.timeit(lambda: list(odx.obj), number=1)}')
print(f'OrderedDictRaymond: {timeit.timeit(lambda: list(od_raymond.obj), number=1)}')
print(f'OrderedDict: {timeit.timeit(lambda: list(od_splice.obj), number=1)}')
print('== in ==')
print(f'OrderedDictX: {timeit.timeit(lambda: [k in odx.obj for k in keys_odx], number=1)}')
print(f'OrderedDictRaymond: {timeit.timeit(lambda: [k in od_raymond.obj for k in keys_raymond], number=1)}')
print(f'OrderedDict: {timeit.timeit(lambda: [k in od_splice.obj for k in keys_splice], number=1)}')
print('== get ==')
print(f'OrderedDictX: {timeit.timeit(lambda: [odx.obj[k] for k in keys_odx], number=1)}')
print(f'OrderedDictRaymond: {timeit.timeit(lambda: [od_raymond.obj[k] for k in keys_raymond], number=1)}')
print(f'OrderedDict: {timeit.timeit(lambda: [od_splice.obj[k] for k in keys_splice], number=1)}')
解决方案 25:
我还没有看到这个确切的答案:
dict['key'] = value
你甚至可以对对象属性执行此操作。通过执行以下操作将它们放入字典中:
dict = vars(obj)
然后,您可以像操作字典一样操作对象属性:
dict['attribute'] = value