标签归档:dictionary

检查字典中是否已存在给定键

问题:检查字典中是否已存在给定键

我想在更新密钥值之前测试字典中是否存在密钥。我写了以下代码:

if 'key1' in dict.keys():
  print "blah"
else:
  print "boo"

我认为这不是完成此任务的最佳方法。有没有更好的方法来测试字典中的键?

I wanted to test if a key exists in a dictionary before updating the value for the key. I wrote the following code:

if 'key1' in dict.keys():
  print "blah"
else:
  print "boo"

I think this is not the best way to accomplish this task. Is there a better way to test for a key in the dictionary?


回答 0

in是测试密钥是否存在的预期方法dict

d = {"key1": 10, "key2": 23}

if "key1" in d:
    print("this will execute")

if "nonexistent key" in d:
    print("this will not")

如果您想使用默认值,可以随时使用dict.get()

d = dict()

for i in range(100):
    key = i % 10
    d[key] = d.get(key, 0) + 1

如果您想始终确保任何键的默认值,则可以dict.setdefault()重复使用,也可以defaultdictcollections模块中使用,例如:

from collections import defaultdict

d = defaultdict(int)

for i in range(100):
    d[i % 10] += 1

但总的来说,in关键字是最好的方法。

in is the intended way to test for the existence of a key in a dict.

d = {"key1": 10, "key2": 23}

if "key1" in d:
    print("this will execute")

if "nonexistent key" in d:
    print("this will not")

If you wanted a default, you can always use dict.get():

d = dict()

for i in range(100):
    key = i % 10
    d[key] = d.get(key, 0) + 1

and if you wanted to always ensure a default value for any key you can either use dict.setdefault() repeatedly or defaultdict from the collections module, like so:

from collections import defaultdict

d = defaultdict(int)

for i in range(100):
    d[i % 10] += 1

but in general, the in keyword is the best way to do it.


回答 1

您不必呼叫按键:

if 'key1' in dict:
  print("blah")
else:
  print("boo")

这将更快,因为它使用字典的哈希而不是进行线性搜索(调用键可以做到)。

You don’t have to call keys:

if 'key1' in dict:
  print("blah")
else:
  print("boo")

That will be much faster as it uses the dictionary’s hashing as opposed to doing a linear search, which calling keys would do.


回答 2

您可以使用in关键字测试字典中是否存在键:

d = {'a': 1, 'b': 2}
'a' in d # <== evaluates to True
'c' in d # <== evaluates to False

在更改字典之前,在字典中检查键是否存在的常见用途是对值进行默认初始化(例如,如果您的值是列表,并且您想确保可以在其后附加一个空列表)在插入键的第一个值时)。在这种情况下,您可能会发现collections.defaultdict()感兴趣的类型。

在较旧的代码中,您可能还会发现的某些用法has_key(),这是一种不赞成使用的方法,用于检查字典中键的存在(仅使用key_name in dict_name,而不是)。

You can test for the presence of a key in a dictionary, using the in keyword:

d = {'a': 1, 'b': 2}
'a' in d # <== evaluates to True
'c' in d # <== evaluates to False

A common use for checking the existence of a key in a dictionary before mutating it is to default-initialize the value (e.g. if your values are lists, for example, and you want to ensure that there is an empty list to which you can append when inserting the first value for a key). In cases such as those, you may find the collections.defaultdict() type to be of interest.

In older code, you may also find some uses of has_key(), a deprecated method for checking the existence of keys in dictionaries (just use key_name in dict_name, instead).


回答 3

您可以缩短此时间:

if 'key1' in dict:
    ...

但是,这充其量是对化妆品的改善。为什么您认为这不是最好的方法?

You can shorten this:

if 'key1' in dict:
    ...

However, this is at best a cosmetic improvement. Why do you believe this is not the best way?


回答 4

有关快速执行接受的答案的建议方法(10m循环)的其他信息:

  • 'key' in mydict 经过时间1.07秒
  • mydict.get('key') 经过时间1.84秒
  • mydefaultdict['key'] 经过时间1.07秒

因此,建议使用indefaultdict反对get

For additional info on speed execution of the accepted answer’s proposed methods (10m loops):

  • 'key' in mydict elapsed time 1.07 sec
  • mydict.get('key') elapsed time 1.84 sec
  • mydefaultdict['key'] elapsed time 1.07 sec

Therefore using in or defaultdict are recommended against get.


回答 5

我建议改用该setdefault方法。听起来它将满足您的所有要求。

>>> d = {'foo':'bar'}
>>> q = d.setdefault('foo','baz') #Do not override the existing key
>>> print q #The value takes what was originally in the dictionary
bar
>>> print d
{'foo': 'bar'}
>>> r = d.setdefault('baz',18) #baz was never in the dictionary
>>> print r #Now r has the value supplied above
18
>>> print d #The dictionary's been updated
{'foo': 'bar', 'baz': 18}

I would recommend using the setdefault method instead. It sounds like it will do everything you want.

>>> d = {'foo':'bar'}
>>> q = d.setdefault('foo','baz') #Do not override the existing key
>>> print q #The value takes what was originally in the dictionary
bar
>>> print d
{'foo': 'bar'}
>>> r = d.setdefault('baz',18) #baz was never in the dictionary
>>> print r #Now r has the value supplied above
18
>>> print d #The dictionary's been updated
{'foo': 'bar', 'baz': 18}

回答 6

python中的Dictionary具有get(’key’,default)方法。因此,您可以在没有密钥的情况下设置默认值。

values = {...}
myValue = values.get('Key', None)

Dictionary in python has a get(‘key’, default) method. So you can just set a default value in case there is no key.

values = {...}
myValue = values.get('Key', None)

回答 7

如何使用EAFP(比请求更容易获得宽恕):

try:
   blah = dict["mykey"]
   # key exists in dict
except KeyError:
   # key doesn't exist in dict

查看其他SO帖子:

使用try VS如果Python

在Python中检查成员是否存在

What about using EAFP (easier to ask forgiveness than permission):

try:
   blah = dict["mykey"]
   # key exists in dict
except KeyError:
   # key doesn't exist in dict

See other SO posts:

Using try vs if in python or

Checking for member existence in Python


回答 8

使用三元运算符:

message = "blah" if 'key1' in dict else "booh"
print(message)

Using ternary operator:

message = "blah" if 'key1' in dict else "booh"
print(message)

回答 9

获得结果的方法是:

哪个更好取决于三个因素:

  1. 字典“通常没有钥匙”还是“通常没有钥匙”。
  2. 您是否打算使用if … else … elseif … else之类的条件?
  3. 字典有多大?

了解更多:http : //paltman.com/try-except-performance-in-python-a-simple-test/

使用try / block代替“ in”或“ if”:

try:
    my_dict_of_items[key_i_want_to_check]
except KeyError:
    # Do the operation you wanted to do for "key not present in dict".
else:
    # Do the operation you wanted to do with "key present in dict."

The ways in which you can get the results are:

Which is better is dependent on 3 things:

  1. Does the dictionary ‘normally has the key’ or ‘normally does not have the key’.
  2. Do you intend to use conditions like if…else…elseif…else?
  3. How big is dictionary?

Read More: http://paltman.com/try-except-performance-in-python-a-simple-test/

Use of try/block instead of ‘in’ or ‘if’:

try:
    my_dict_of_items[key_i_want_to_check]
except KeyError:
    # Do the operation you wanted to do for "key not present in dict".
else:
    # Do the operation you wanted to do with "key present in dict."

回答 10

仅限于Python 2 :(并且python 2.7 in已经支持)

您可以使用has_key()方法:

if dict.has_key('xyz')==1:
    #update the value for the key
else:
    pass

Python 2 only: (and python 2.7 supports in already)

you can use the has_key() method:

if dict.has_key('xyz')==1:
    #update the value for the key
else:
    pass

回答 11

只是克里斯的补充。B(最佳答案):

d = defaultdict(int)

也可以;原因是调用int()返回0defaultdict后台操作(在构造字典时),因此在文档中称为“工厂功能”。

Just an FYI adding to Chris. B (best answer):

d = defaultdict(int)

Works as well; the reason is that calling int() returns 0 which is what defaultdict does behind the scenes (when constructing a dictionary), hence the name “Factory Function” in the documentation.


回答 12

检查字典中是否已存在给定键

为了了解如何做到这一点,我们首先检查可以在字典上调用哪些方法。方法如下:

d={'clear':0, 'copy':1, 'fromkeys':2, 'get':3, 'items':4, 'keys':5, 'pop':6, 'popitem':7, 'setdefault':8, 'update':9, 'values':10}

Python Dictionary clear()       Removes all Items
Python Dictionary copy()        Returns Shallow Copy of a Dictionary
Python Dictionary fromkeys()    Creates dictionary from given sequence
Python Dictionary get()         Returns Value of The Key
Python Dictionary items()       Returns view of dictionary (key, value) pair
Python Dictionary keys()        Returns View Object of All Keys
Python Dictionary pop()         Removes and returns element having given key
Python Dictionary popitem()     Returns & Removes Element From Dictionary
Python Dictionary setdefault()  Inserts Key With a Value if Key is not Present
Python Dictionary update()      Updates the Dictionary 
Python Dictionary values()      Returns view of all values in dictionary

检查密钥是否已存在的残酷方法可能是get()

d.get("key")

其他两种有趣的方法items()keys()听起来工作量太大。因此,让我们检查一下get()是否适合我们。我们有我们的字典d

d= {'clear':0, 'copy':1, 'fromkeys':2, 'get':3, 'items':4, 'keys':5, 'pop':6, 'popitem':7, 'setdefault':8, 'update':9, 'values':10}

打印显示我们没有的密钥将返回None

print(d.get('key')) #None
print(d.get('clear')) #0
print(d.get('copy')) #1

如果密钥存在或不存在,我们可能会用它来获取信息。但是,如果我们使用单个命令创建字典,请考虑以下问题key:None

d= {'key':None}
print(d.get('key')) #None
print(d.get('key2')) #None

get()如果某些值可能是,导致该方法不可靠None。这个故事的结局应该更快乐。如果我们使用in比较器:

print('key' in d) #True
print('key2' in d) #False

我们得到正确的结果。我们可以检查一下Python字节码:

import dis
dis.dis("'key' in d")
#   1           0 LOAD_CONST               0 ('key')
#               2 LOAD_NAME                0 (d)
#               4 COMPARE_OP               6 (in)
#               6 RETURN_VALUE

dis.dis("d.get('key2')")
#   1           0 LOAD_NAME                0 (d)
#               2 LOAD_METHOD              1 (get)
#               4 LOAD_CONST               0 ('key2')
#               6 CALL_METHOD              1
#               8 RETURN_VALUE

这表明in比较运算符不仅比更加可靠,而且甚至更快get()

Check if a given key already exists in a dictionary

To get the idea how to do that we first inspect what methods we can call on dictionary. Here are the methods:

d={'clear':0, 'copy':1, 'fromkeys':2, 'get':3, 'items':4, 'keys':5, 'pop':6, 'popitem':7, 'setdefault':8, 'update':9, 'values':10}

Python Dictionary clear()       Removes all Items
Python Dictionary copy()        Returns Shallow Copy of a Dictionary
Python Dictionary fromkeys()    Creates dictionary from given sequence
Python Dictionary get()         Returns Value of The Key
Python Dictionary items()       Returns view of dictionary (key, value) pair
Python Dictionary keys()        Returns View Object of All Keys
Python Dictionary pop()         Removes and returns element having given key
Python Dictionary popitem()     Returns & Removes Element From Dictionary
Python Dictionary setdefault()  Inserts Key With a Value if Key is not Present
Python Dictionary update()      Updates the Dictionary 
Python Dictionary values()      Returns view of all values in dictionary

The brutal method to check if the key already exists may be the get() method:

d.get("key")

The other two interesting methods items() and keys() sounds like too much of work. So let’s examine if get() is the right method for us. We have our dict d:

d= {'clear':0, 'copy':1, 'fromkeys':2, 'get':3, 'items':4, 'keys':5, 'pop':6, 'popitem':7, 'setdefault':8, 'update':9, 'values':10}

Printing shows the key we don’t have will return None:

print(d.get('key')) #None
print(d.get('clear')) #0
print(d.get('copy')) #1

We may use that to get the info if the key is present or no. But consider this if we create a dict with a single key:None:

d= {'key':None}
print(d.get('key')) #None
print(d.get('key2')) #None

Leading that get() method is not reliable in case some values may be None. This story should have a happier ending. If we use the in comparator:

print('key' in d) #True
print('key2' in d) #False

We get the correct results. We may examine the Python byte code:

import dis
dis.dis("'key' in d")
#   1           0 LOAD_CONST               0 ('key')
#               2 LOAD_NAME                0 (d)
#               4 COMPARE_OP               6 (in)
#               6 RETURN_VALUE

dis.dis("d.get('key2')")
#   1           0 LOAD_NAME                0 (d)
#               2 LOAD_METHOD              1 (get)
#               4 LOAD_CONST               0 ('key2')
#               6 CALL_METHOD              1
#               8 RETURN_VALUE

This shows that in compare operator is not just more reliable but even faster than get().


回答 13

Python字典具有称为的方法__contains__。如果字典具有键,则此方法将返回True,否则返回False。

 >>> temp = {}

 >>> help(temp.__contains__)

Help on built-in function __contains__:

__contains__(key, /) method of builtins.dict instance
    True if D has a key k, else False.

Python dictionary has the method called __contains__. This method will return True if the dictionary has the key else returns False.

 >>> temp = {}

 >>> help(temp.__contains__)

Help on built-in function __contains__:

__contains__(key, /) method of builtins.dict instance
    True if D has a key k, else False.

回答 14

共享使用布尔运算符检查密钥是否存在的另一种方法。

d = {'a': 1, 'b':2}
keys = 'abcd'

for k in keys:
    x = (k in d and 'blah') or 'boo'
    print(x) 

这返回

>>> blah
>>> blah
>>> boo
>>> boo

说明

首先,你应该知道,在Python, ,0None或长度为零的对象评估为False。其他所有内容的计算结果均为True。布尔运算从左到右求值,并且返回的操作数不是True或False。

让我们来看一个例子:

>>> 'Some string' or 1/0 
'Some string'
>>>

由于'Some string'评估为True,因此or不会评估其余的,因此不会产生除以零的误差。

但是,如果我们切换顺序,1/0则会首先评估订单并引发异常:

>>> 1/0 or 'Some string'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
>>> 

我们可以使用此模式检查密钥是否存在。

(k in d and 'blah')

与…相同

if k in d:
    'blah'
else:
    False

如果键存在,这已经返回了正确的结果,但是我们希望它在不存在时打印“ boo”。因此,我们将结果or'boo'

>>> False or 'boo'
'boo'
>>> 'blah' or 'boo'
'blah'
>>> 

Sharing one more way of checking if a key exists using boolean operators.

d = {'a': 1, 'b':2}
keys = 'abcd'

for k in keys:
    x = (k in d and 'blah') or 'boo'
    print(x) 

This returns

>>> blah
>>> blah
>>> boo
>>> boo

Explanation

First you should know that in Python, 0, None, or objects with zero length evaluate to False. Everything else evaluates to True. Boolean operations are evaluated left to right and return the operand not True or False.

Let’s see an example:

>>> 'Some string' or 1/0 
'Some string'
>>>

Since 'Some string' evaluates to True, the rest of the or is not evaluated and there is no division by zero error raised.

But if we switch the order 1/0 is evaluated first and raises an exception:

>>> 1/0 or 'Some string'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
>>> 

We can use this for pattern for checking if a key exists.

(k in d and 'blah')

does the same as

if k in d:
    'blah'
else:
    False

This already returns the correct result if the key exists, but we want it to print ‘boo’ when it doesn’t. So, we take the result and or it with 'boo'

>>> False or 'boo'
'boo'
>>> 'blah' or 'boo'
'blah'
>>> 

回答 15

您可以使用for循环遍历字典并获取要在字典中找到的键的名称,然后使用if条件检查其是否存在:

dic = {'first' : 12, 'second' : 123}
for each in dic:
    if each == 'second': 
        print('the key exists and the corresponding value can be updated in the dictionary')

You can use for loop to iterate over the dictionary and get the name of key you want to find in the dictionary, after that check if it exist or not using if condition:

dic = {'first' : 12, 'second' : 123}
for each in dic:
    if each == 'second': 
        print('the key exists and the corresponding value can be updated in the dictionary')

如何向字典添加新键?

问题:如何向字典添加新键?

创建字典后是否可以向Python字典添加关键字?

它似乎没有.add()方法。

Is it possible to add a key to a Python dictionary after it has been created?

It doesn’t seem to have an .add() method.


回答 0

d = {'key': 'value'}
print(d)
# {'key': 'value'}
d['mynewkey'] = 'mynewvalue'
print(d)
# {'key': 'value', 'mynewkey': 'mynewvalue'}
d = {'key': 'value'}
print(d)
# {'key': 'value'}
d['mynewkey'] = 'mynewvalue'
print(d)
# {'key': 'value', 'mynewkey': 'mynewvalue'}

回答 1

要同时添加多个键,请使用dict.update()

>>> x = {1:2}
>>> print(x)
{1: 2}

>>> d = {3:4, 5:6, 7:8}
>>> x.update(d)
>>> print(x)
{1: 2, 3: 4, 5: 6, 7: 8}

对于添加单个密钥,可接受的答案具有较少的计算开销。

To add multiple keys simultaneously, use dict.update():

>>> x = {1:2}
>>> print(x)
{1: 2}

>>> d = {3:4, 5:6, 7:8}
>>> x.update(d)
>>> print(x)
{1: 2, 3: 4, 5: 6, 7: 8}

For adding a single key, the accepted answer has less computational overhead.


回答 2

我想整合有关Python字典的信息:

创建一个空字典

data = {}
# OR
data = dict()

用初始值创建字典

data = {'a': 1, 'b': 2, 'c': 3}
# OR
data = dict(a=1, b=2, c=3)
# OR
data = {k: v for k, v in (('a', 1), ('b',2), ('c',3))}

插入/更新单个值

data['a'] = 1  # Updates if 'a' exists, else adds 'a'
# OR
data.update({'a': 1})
# OR
data.update(dict(a=1))
# OR
data.update(a=1)

插入/更新多个值

data.update({'c':3,'d':4})  # Updates 'c' and adds 'd'

创建合并字典而无需修改原始字典

data3 = {}
data3.update(data)  # Modifies data3, not data
data3.update(data2)  # Modifies data3, not data2

删除字典中的项目

del data[key]  # Removes specific element in a dictionary
data.pop(key)  # Removes the key & returns the value
data.clear()  # Clears entire dictionary

检查密钥是否已在字典中

key in data

遍历字典中的对

for key in data: # Iterates just through the keys, ignoring the values
for key, value in d.items(): # Iterates through the pairs
for key in d.keys(): # Iterates just through key, ignoring the values
for value in d.values(): # Iterates just through value, ignoring the keys

从两个列表创建字典

data = dict(zip(list_with_keys, list_with_values))

Python 3.5的新功能

创建合并字典而不修改原始字典:

这使用了称为字典解包的新功能。

data = {**data1, **data2, **data3}

Python 3.9的新功能

更新或添加现有字典的值

现在,更新运算符 |=可用于词典:

data |= {'c':3,'d':4}

创建合并字典而无需修改原始字典

合并操作 |现在工作的字典:

data = data1 | {'c':3,'d':4}

随时添加更多!

I feel like consolidating info about Python dictionaries:

Creating an empty dictionary

data = {}
# OR
data = dict()

Creating a dictionary with initial values

data = {'a': 1, 'b': 2, 'c': 3}
# OR
data = dict(a=1, b=2, c=3)
# OR
data = {k: v for k, v in (('a', 1), ('b',2), ('c',3))}

Inserting/Updating a single value

data['a'] = 1  # Updates if 'a' exists, else adds 'a'
# OR
data.update({'a': 1})
# OR
data.update(dict(a=1))
# OR
data.update(a=1)

Inserting/Updating multiple values

data.update({'c':3,'d':4})  # Updates 'c' and adds 'd'

Creating a merged dictionary without modifying originals

data3 = {}
data3.update(data)  # Modifies data3, not data
data3.update(data2)  # Modifies data3, not data2

Deleting items in dictionary

del data[key]  # Removes specific element in a dictionary
data.pop(key)  # Removes the key & returns the value
data.clear()  # Clears entire dictionary

Check if a key is already in dictionary

key in data

Iterate through pairs in a dictionary

for key in data: # Iterates just through the keys, ignoring the values
for key, value in d.items(): # Iterates through the pairs
for key in d.keys(): # Iterates just through key, ignoring the values
for value in d.values(): # Iterates just through value, ignoring the keys

Create a dictionary from two lists

data = dict(zip(list_with_keys, list_with_values))

New to Python 3.5

Creating a merged dictionary without modifying originals:

This uses a new featrue called dictionary unpacking.

data = {**data1, **data2, **data3}

New to Python 3.9

Update or add values for an existing dictionary

The update operator |= now works for dictionaries:

data |= {'c':3,'d':4}

Creating a merged dictionary without modifying originals

The merge operator | now works for dictionaries:

data = data1 | {'c':3,'d':4}

Feel free to add more!


回答 3

“创建密钥后是否可以向Python字典添加密钥?它似乎没有.add()方法。”

是的,这是可能的,并且它确实具有实现此目的的方法,但是您不想直接使用它。

为了演示如何以及如何不使用它,让我们用dict文字创建一个空的dict {}

my_dict = {}

最佳实践1:下标符号

要使用单个新键和值更新此字典,可以使用下标符号(请参见此处的映射)提供项目分配:

my_dict['new key'] = 'new value'

my_dict 就是现在:

{'new key': 'new value'}

最佳实践2:update方法-2种方法

我们也可以使用update方法高效地使用多个值更新字典。我们可能在dict这里不必要地创建了一个额外的东西,因此我们希望我们dict已经被创建并来自另一个目的或用于另一个目的:

my_dict.update({'key 2': 'value 2', 'key 3': 'value 3'})

my_dict 就是现在:

{'key 2': 'value 2', 'key 3': 'value 3', 'new key': 'new value'}

使用update方法执行此操作的另一种有效方法是使用关键字参数,但是由于它们必须是合法的python单词,因此您不能使用空格或特殊符号或以数字开头的名称,但是许多人认为这是更易读的方法为字典创建键,在这里我们当然避免创建额外的不必要的键dict

my_dict.update(foo='bar', foo2='baz')

my_dict现在是:

{'key 2': 'value 2', 'key 3': 'value 3', 'new key': 'new value', 
 'foo': 'bar', 'foo2': 'baz'}

因此,现在我们介绍了三种更新Python的Python方法dict


魔术方法,__setitem__以及为什么应避免使用

还有另一种dict您不应该使用的更新__setitem__方法,它使用的方法。这是一个示例,说明了如何使用该__setitem__方法将键值对添加到dict,并演示了使用它的不良性能:

>>> d = {}
>>> d.__setitem__('foo', 'bar')
>>> d
{'foo': 'bar'}


>>> def f():
...     d = {}
...     for i in xrange(100):
...         d['foo'] = i
... 
>>> def g():
...     d = {}
...     for i in xrange(100):
...         d.__setitem__('foo', i)
... 
>>> import timeit
>>> number = 100
>>> min(timeit.repeat(f, number=number))
0.0020880699157714844
>>> min(timeit.repeat(g, number=number))
0.005071878433227539

因此,我们看到使用下标符号实际上比使用下标符号要快得多__setitem__。做Python式的事情,也就是说,按照预期的方式使用该语言,通常既可读性强又计算效率高。

“Is it possible to add a key to a Python dictionary after it has been created? It doesn’t seem to have an .add() method.”

Yes it is possible, and it does have a method that implements this, but you don’t want to use it directly.

To demonstrate how and how not to use it, let’s create an empty dict with the dict literal, {}:

my_dict = {}

Best Practice 1: Subscript notation

To update this dict with a single new key and value, you can use the subscript notation (see Mappings here) that provides for item assignment:

my_dict['new key'] = 'new value'

my_dict is now:

{'new key': 'new value'}

Best Practice 2: The update method – 2 ways

We can also update the dict with multiple values efficiently as well using the update method. We may be unnecessarily creating an extra dict here, so we hope our dict has already been created and came from or was used for another purpose:

my_dict.update({'key 2': 'value 2', 'key 3': 'value 3'})

my_dict is now:

{'key 2': 'value 2', 'key 3': 'value 3', 'new key': 'new value'}

Another efficient way of doing this with the update method is with keyword arguments, but since they have to be legitimate python words, you can’t have spaces or special symbols or start the name with a number, but many consider this a more readable way to create keys for a dict, and here we certainly avoid creating an extra unnecessary dict:

my_dict.update(foo='bar', foo2='baz')

and my_dict is now:

{'key 2': 'value 2', 'key 3': 'value 3', 'new key': 'new value', 
 'foo': 'bar', 'foo2': 'baz'}

So now we have covered three Pythonic ways of updating a dict.


Magic method, __setitem__, and why it should be avoided

There’s another way of updating a dict that you shouldn’t use, which uses the __setitem__ method. Here’s an example of how one might use the __setitem__ method to add a key-value pair to a dict, and a demonstration of the poor performance of using it:

>>> d = {}
>>> d.__setitem__('foo', 'bar')
>>> d
{'foo': 'bar'}


>>> def f():
...     d = {}
...     for i in xrange(100):
...         d['foo'] = i
... 
>>> def g():
...     d = {}
...     for i in xrange(100):
...         d.__setitem__('foo', i)
... 
>>> import timeit
>>> number = 100
>>> min(timeit.repeat(f, number=number))
0.0020880699157714844
>>> min(timeit.repeat(g, number=number))
0.005071878433227539

So we see that using the subscript notation is actually much faster than using __setitem__. Doing the Pythonic thing, that is, using the language in the way it was intended to be used, usually is both more readable and computationally efficient.


回答 4

dictionary[key] = value
dictionary[key] = value

回答 5

如果要在字典中添加字典,可以使用此方法。

示例:将新条目添加到词典和子词典中

dictionary = {}
dictionary["new key"] = "some new entry" # add new dictionary entry
dictionary["dictionary_within_a_dictionary"] = {} # this is required by python
dictionary["dictionary_within_a_dictionary"]["sub_dict"] = {"other" : "dictionary"}
print (dictionary)

输出:

{'new key': 'some new entry', 'dictionary_within_a_dictionary': {'sub_dict': {'other': 'dictionarly'}}}

注意: Python要求您首先添加一个子

dictionary["dictionary_within_a_dictionary"] = {}

在添加条目之前。

If you want to add a dictionary within a dictionary you can do it this way.

Example: Add a new entry to your dictionary & sub dictionary

dictionary = {}
dictionary["new key"] = "some new entry" # add new dictionary entry
dictionary["dictionary_within_a_dictionary"] = {} # this is required by python
dictionary["dictionary_within_a_dictionary"]["sub_dict"] = {"other" : "dictionary"}
print (dictionary)

Output:

{'new key': 'some new entry', 'dictionary_within_a_dictionary': {'sub_dict': {'other': 'dictionarly'}}}

NOTE: Python requires that you first add a sub

dictionary["dictionary_within_a_dictionary"] = {}

before adding entries.


回答 6

正统语法为d[key] = value,但是如果键盘缺少方括号键,则可以执行以下操作:

d.__setitem__(key, value)

实际上,定义__getitem____setitem__方法是使自己的类支持方括号语法的方法。参见https://python.developpez.com/cours/DiveIntoPython/php/endiveintopython/object_linked_framework/special_class_methods.php

The orthodox syntax is d[key] = value, but if your keyboard is missing the square bracket keys you could do:

d.__setitem__(key, value)

In fact, defining __getitem__ and __setitem__ methods is how you can make your own class support the square bracket syntax. See https://python.developpez.com/cours/DiveIntoPython/php/endiveintopython/object_oriented_framework/special_class_methods.php


回答 7

您可以创建一个:

class myDict(dict):

    def __init__(self):
        self = dict()

    def add(self, key, value):
        self[key] = value

## example

myd = myDict()
myd.add('apples',6)
myd.add('bananas',3)
print(myd)

给出:

>>> 
{'apples': 6, 'bananas': 3}

You can create one:

class myDict(dict):

    def __init__(self):
        self = dict()

    def add(self, key, value):
        self[key] = value

## example

myd = myDict()
myd.add('apples',6)
myd.add('bananas',3)
print(myd)

Gives:

>>> 
{'apples': 6, 'bananas': 3}

回答 8

这个受欢迎的问题解决了合并词典和语法的功能方法。ab

以下是一些更简单的方法(已在Python 3中测试)…

c = dict( a, **b ) ## see also https://stackoverflow.com/q/2255878
c = dict( list(a.items()) + list(b.items()) )
c = dict( i for d in [a,b] for i in d.items() )

注意:以上第一种方法仅在输入的键b为字符串时才有效。

要添加或修改单个元素b字典将仅包含一个元素…

c = dict( a, **{'d':'dog'} ) ## returns a dictionary based on 'a'

这相当于…

def functional_dict_add( dictionary, key, value ):
   temp = dictionary.copy()
   temp[key] = value
   return temp

c = functional_dict_add( a, 'd', 'dog' )

This popular question addresses functional methods of merging dictionaries a and b.

Here are some of the more straightforward methods (tested in Python 3)…

c = dict( a, **b ) ## see also https://stackoverflow.com/q/2255878
c = dict( list(a.items()) + list(b.items()) )
c = dict( i for d in [a,b] for i in d.items() )

Note: The first method above only works if the keys in b are strings.

To add or modify a single element, the b dictionary would contain only that one element…

c = dict( a, **{'d':'dog'} ) ## returns a dictionary based on 'a'

This is equivalent to…

def functional_dict_add( dictionary, key, value ):
   temp = dictionary.copy()
   temp[key] = value
   return temp

c = functional_dict_add( a, 'd', 'dog' )

回答 9

假设您想生活在不可变的世界中,不想修改原始文件,而是想创建一个新文件dict,这是向原始文件添加新密钥的结果。

在Python 3.5+中,您可以执行以下操作:

params = {'a': 1, 'b': 2}
new_params = {**params, **{'c': 3}}

Python 2等效项是:

params = {'a': 1, 'b': 2}
new_params = dict(params, **{'c': 3})

在这两个之后:

params 仍然等于 {'a': 1, 'b': 2}

new_params 等于 {'a': 1, 'b': 2, 'c': 3}

有时候,您不想修改原始文件(您只想要添加到原始文件的结果)。我发现这可以替代以下内容:

params = {'a': 1, 'b': 2}
new_params = params.copy()
new_params['c'] = 3

要么

params = {'a': 1, 'b': 2}
new_params = params.copy()
new_params.update({'c': 3})

参考:https : //stackoverflow.com/a/2255892/514866

Let’s pretend you want to live in the immutable world and do NOT want to modify the original but want to create a new dict that is the result of adding a new key to the original.

In Python 3.5+ you can do:

params = {'a': 1, 'b': 2}
new_params = {**params, **{'c': 3}}

The Python 2 equivalent is:

params = {'a': 1, 'b': 2}
new_params = dict(params, **{'c': 3})

After either of these:

params is still equal to {'a': 1, 'b': 2}

and

new_params is equal to {'a': 1, 'b': 2, 'c': 3}

There will be times when you don’t want to modify the original (you only want the result of adding to the original). I find this a refreshing alternative to the following:

params = {'a': 1, 'b': 2}
new_params = params.copy()
new_params['c'] = 3

or

params = {'a': 1, 'b': 2}
new_params = params.copy()
new_params.update({'c': 3})

Reference: https://stackoverflow.com/a/2255892/514866


回答 10

如此众多的答案,仍然让每个人都忘记了这个名字奇怪,举止古怪而又方便的地方 dict.setdefault()

这个

value = my_dict.setdefault(key, default)

基本上就是这样做:

try:
    value = my_dict[key]
except KeyError: # key not found
    value = my_dict[key] = default

例如

>>> mydict = {'a':1, 'b':2, 'c':3}
>>> mydict.setdefault('d', 4)
4 # returns new value at mydict['d']
>>> print(mydict)
{'a':1, 'b':2, 'c':3, 'd':4} # a new key/value pair was indeed added
# but see what happens when trying it on an existing key...
>>> mydict.setdefault('a', 111)
1 # old value was returned
>>> print(mydict)
{'a':1, 'b':2, 'c':3, 'd':4} # existing key was ignored

So many answers and still everybody forgot about the strangely named, oddly behaved, and yet still handy dict.setdefault()

This

value = my_dict.setdefault(key, default)

basically just does this:

try:
    value = my_dict[key]
except KeyError: # key not found
    value = my_dict[key] = default

e.g.

>>> mydict = {'a':1, 'b':2, 'c':3}
>>> mydict.setdefault('d', 4)
4 # returns new value at mydict['d']
>>> print(mydict)
{'a':1, 'b':2, 'c':3, 'd':4} # a new key/value pair was indeed added
# but see what happens when trying it on an existing key...
>>> mydict.setdefault('a', 111)
1 # old value was returned
>>> print(mydict)
{'a':1, 'b':2, 'c':3, 'd':4} # existing key was ignored

回答 11

如果您不加入两个字典,而是将新的键值对添加到字典中,那么使用下标表示法似乎是最好的方法。

import timeit

timeit.timeit('dictionary = {"karga": 1, "darga": 2}; dictionary.update({"aaa": 123123, "asd": 233})')
>> 0.49582505226135254

timeit.timeit('dictionary = {"karga": 1, "darga": 2}; dictionary["aaa"] = 123123; dictionary["asd"] = 233;')
>> 0.20782899856567383

但是,例如,如果您想添加数千个新的键值对,则应考虑使用该update()方法。

If you’re not joining two dictionaries, but adding new key-value pairs to a dictionary, then using the subscript notation seems like the best way.

import timeit

timeit.timeit('dictionary = {"karga": 1, "darga": 2}; dictionary.update({"aaa": 123123, "asd": 233})')
>> 0.49582505226135254

timeit.timeit('dictionary = {"karga": 1, "darga": 2}; dictionary["aaa"] = 123123; dictionary["asd"] = 233;')
>> 0.20782899856567383

However, if you’d like to add, for example, thousands of new key-value pairs, you should consider using the update() method.


回答 12

我认为collections指出由许多有用的字典子类和包装器组成的Python 模块也是有用的,这些子类和包装器简化了字典中数据类型添加和修改,特别是defaultdict

dict子类,调用工厂函数以提供缺失值

如果要使用始终由相同数据类型或结构组成的字典(例如列表的字典),这将特别有用。

>>> from collections import defaultdict
>>> example = defaultdict(int)
>>> example['key'] += 1
>>> example['key']
defaultdict(<class 'int'>, {'key': 1})

如果键尚不存在,defaultdict则将给定的值(在我们的例子中10)分配为字典的初始值(通常在循环中使用)。因此,此操作有两件事:将一个新的键添加到字典中(按问题),如果该键尚不存在,则分配值。使用标准字典,这将在+=操作尝试访问尚不存在的值时引发错误:

>>> example = dict()
>>> example['key'] += 1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'key'

如果不使用defaultdict,则添加新元素的代码量会大得多,并且可能看起来像这样:

# This type of code would often be inside a loop
if 'key' not in example:
    example['key'] = 0  # add key and initial value to dict; could also be a list
example['key'] += 1  # this is implementing a counter

defaultdict也可以用于复杂的数据类型,例如listset

>>> example = defaultdict(list)
>>> example['key'].append(1)
>>> example
defaultdict(<class 'list'>, {'key': [1]})

添加元素会自动初始化列表。

I think it would also be useful to point out Python’s collections module that consists of many useful dictionary subclasses and wrappers that simplify the addition and modification of data types in a dictionary, specifically defaultdict:

dict subclass that calls a factory function to supply missing values

This is particularly useful if you are working with dictionaries that always consist of the same data types or structures, for example a dictionary of lists.

>>> from collections import defaultdict
>>> example = defaultdict(int)
>>> example['key'] += 1
>>> example['key']
defaultdict(<class 'int'>, {'key': 1})

If the key does not yet exist, defaultdict assigns the value given (in our case 10) as the initial value to the dictionary (often used inside loops). This operation therefore does two things: it adds a new key to a dictionary (as per question), and assigns the value if the key doesn’t yet exist. With the standard dictionary, this would have raised an error as the += operation is trying to access a value that doesn’t yet exist:

>>> example = dict()
>>> example['key'] += 1
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'key'

Without the use of defaultdict, the amount of code to add a new element would be much greater and perhaps looks something like:

# This type of code would often be inside a loop
if 'key' not in example:
    example['key'] = 0  # add key and initial value to dict; could also be a list
example['key'] += 1  # this is implementing a counter

defaultdict can also be used with complex data types such as list and set:

>>> example = defaultdict(list)
>>> example['key'].append(1)
>>> example
defaultdict(<class 'list'>, {'key': [1]})

Adding an element automatically initialises the list.


回答 13

这是我在这里没有看到的另一种方式:

>>> foo = dict(a=1,b=2)
>>> foo
{'a': 1, 'b': 2}
>>> goo = dict(c=3,**foo)
>>> goo
{'c': 3, 'a': 1, 'b': 2}

您可以使用字典构造函数和隐式扩展来重建字典。此外,有趣的是,此方法可用于控制字典构建过程中的位置顺序(在Python 3.6之后)。实际上,Python 3.7及更高版本保证了插入顺序!

>>> foo = dict(a=1,b=2,c=3,d=4)
>>> new_dict = {k: v for k, v in list(foo.items())[:2]}
>>> new_dict
{'a': 1, 'b': 2}
>>> new_dict.update(newvalue=99)
>>> new_dict
{'a': 1, 'b': 2, 'newvalue': 99}
>>> new_dict.update({k: v for k, v in list(foo.items())[2:]})
>>> new_dict
{'a': 1, 'b': 2, 'newvalue': 99, 'c': 3, 'd': 4}
>>> 

上面是使用字典理解的。

Here’s another way that I didn’t see here:

>>> foo = dict(a=1,b=2)
>>> foo
{'a': 1, 'b': 2}
>>> goo = dict(c=3,**foo)
>>> goo
{'c': 3, 'a': 1, 'b': 2}

You can use the dictionary constructor and implicit expansion to reconstruct a dictionary. Moreover, interestingly, this method can be used to control the positional order during dictionary construction (post Python 3.6). In fact, insertion order is guaranteed for Python 3.7 and above!

>>> foo = dict(a=1,b=2,c=3,d=4)
>>> new_dict = {k: v for k, v in list(foo.items())[:2]}
>>> new_dict
{'a': 1, 'b': 2}
>>> new_dict.update(newvalue=99)
>>> new_dict
{'a': 1, 'b': 2, 'newvalue': 99}
>>> new_dict.update({k: v for k, v in list(foo.items())[2:]})
>>> new_dict
{'a': 1, 'b': 2, 'newvalue': 99, 'c': 3, 'd': 4}
>>> 

The above is using dictionary comprehension.


回答 14

首先检查密钥是否已经存在

a={1:2,3:4}
a.get(1)
2
a.get(5)
None

然后您可以添加新的键和值

first to check whether the key already exists

a={1:2,3:4}
a.get(1)
2
a.get(5)
None

then you can add the new key and value


回答 15

添加字典键,值类。

class myDict(dict):

    def __init__(self):
        self = dict()

    def add(self, key, value):
        #self[key] = value # add new key and value overwriting any exiting same key
        if self.get(key)!=None:
            print('key', key, 'already used') # report if key already used
        self.setdefault(key, value) # if key exit do nothing


## example

myd = myDict()
name = "fred"

myd.add('apples',6)
print('\n', myd)
myd.add('bananas',3)
print('\n', myd)
myd.add('jack', 7)
print('\n', myd)
myd.add(name, myd)
print('\n', myd)
myd.add('apples', 23)
print('\n', myd)
myd.add(name, 2)
print(myd)

add dictionary key, value class.

class myDict(dict):

    def __init__(self):
        self = dict()

    def add(self, key, value):
        #self[key] = value # add new key and value overwriting any exiting same key
        if self.get(key)!=None:
            print('key', key, 'already used') # report if key already used
        self.setdefault(key, value) # if key exit do nothing


## example

myd = myDict()
name = "fred"

myd.add('apples',6)
print('\n', myd)
myd.add('bananas',3)
print('\n', myd)
myd.add('jack', 7)
print('\n', myd)
myd.add(name, myd)
print('\n', myd)
myd.add('apples', 23)
print('\n', myd)
myd.add(name, 2)
print(myd)

如何在Python中的单个表达式中合并两个字典?

问题:如何在Python中的单个表达式中合并两个字典?

我有两个Python字典,我想编写一个返回合并的这两个字典的表达式。update()如果返回结果而不是就地修改字典,该方法将是我所需要的。

>>> x = {'a': 1, 'b': 2}
>>> y = {'b': 10, 'c': 11}
>>> z = x.update(y)
>>> print(z)
None
>>> x
{'a': 1, 'b': 10, 'c': 11}

我怎样才能在最终的合并字典z,不是x

(更清楚地说,dict.update()我正在寻找的最后一个胜出的冲突处理方法是。)

I have two Python dictionaries, and I want to write a single expression that returns these two dictionaries, merged. The update() method would be what I need, if it returned its result instead of modifying a dictionary in-place.

>>> x = {'a': 1, 'b': 2}
>>> y = {'b': 10, 'c': 11}
>>> z = x.update(y)
>>> print(z)
None
>>> x
{'a': 1, 'b': 10, 'c': 11}

How can I get that final merged dictionary in z, not x?

(To be extra-clear, the last-one-wins conflict-handling of dict.update() is what I’m looking for as well.)


回答 0

如何在一个表达式中合并两个Python字典?

对于字典xyz变成了浅层合并的字典,带有y替换的值x

  • 在Python 3.5或更高版本中:

    z = {**x, **y}
  • 在Python 2(或3.4或更低版本)中,编写一个函数:

    def merge_two_dicts(x, y):
        z = x.copy()   # start with x's keys and values
        z.update(y)    # modifies z with y's keys and values & returns None
        return z

    现在:

    z = merge_two_dicts(x, y)
  • 在Python 3.9.0a4以上(最终发布日期大约2020年10月):PEP-584这里讨论,执行,以进一步简化这一点:

    z = x | y          # NOTE: 3.9+ ONLY

说明

假设您有两个字典,并且想要将它们合并为新字典而不更改原始字典:

x = {'a': 1, 'b': 2}
y = {'b': 3, 'c': 4}

理想的结果是获得一个z合并了值的新字典(),第二个dict的值覆盖第一个字典的值。

>>> z
{'a': 1, 'b': 3, 'c': 4}

PEP 448中提出并从Python 3.5开始可用的新语法是

z = {**x, **y}

它确实是一个表达。

注意,我们也可以使用文字符号合并:

z = {**x, 'foo': 1, 'bar': 2, **y}

现在:

>>> z
{'a': 1, 'b': 3, 'foo': 1, 'bar': 2, 'c': 4}

它现在显示为在3.5发布时间表中实现,PEP 478,并且已进入Python 3.5的新功能文档。

但是,由于许多组织仍在使用Python 2,因此您可能希望以向后兼容的方式进行操作。在Python 2和Python 3.0-3.4中可用的经典Pythonic方法是分两个步骤完成的:

z = x.copy()
z.update(y) # which returns None since it mutates z

在这两种方法中,y将排第二,其值将替换x的值,因此'b'将指向3我们的最终结果。

尚未在Python 3.5上运行,但需要一个表达式

如果您尚未使用Python 3.5,或者需要编写向后兼容的代码,并且希望在单个表达式中使用它,则最有效的方法是将其放入函数中:

def merge_two_dicts(x, y):
    """Given two dicts, merge them into a new dict as a shallow copy."""
    z = x.copy()
    z.update(y)
    return z

然后您有一个表达式:

z = merge_two_dicts(x, y)

您还可以创建一个函数来合并未定义数量的dict,从零到非常大的数量:

def merge_dicts(*dict_args):
    """
    Given any number of dicts, shallow copy and merge into a new dict,
    precedence goes to key value pairs in latter dicts.
    """
    result = {}
    for dictionary in dict_args:
        result.update(dictionary)
    return result

此函数将在Python 2和3中适用于所有字典。例如给以下a命令g

z = merge_dicts(a, b, c, d, e, f, g) 

和中的键值对g优先af,以此类推。

其他答案的批判

不要使用以前接受的答案中看到的内容:

z = dict(x.items() + y.items())

在Python 2中,您将在每个内存字典中创建两个列表,在内存中创建第三个列表,其长度等于前两个字典的长度,然后丢弃所有三个列表以创建字典。在Python 3中,这将失败,因为您将两个dict_items对象而不是两个列表加在一起-

>>> c = dict(a.items() + b.items())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'dict_items' and 'dict_items'

并且您必须将它们明确创建为列表,例如z = dict(list(x.items()) + list(y.items()))。这浪费了资源和计算能力。

类似地,当值是不可散列的对象(例如列表)时,items()在Python 3(viewitems()在Python 2.7中)进行联合也将失败。即使您的值是可哈希的,由于集合在语义上是无序的,因此关于优先级的行为是不确定的。所以不要这样做:

>>> c = dict(a.items() | b.items())

此示例演示了值不可散列时会发生的情况:

>>> x = {'a': []}
>>> y = {'b': []}
>>> dict(x.items() | y.items())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

这是一个示例,其中y应该优先,但是由于集合的任意顺序,保留了x的值:

>>> x = {'a': 2}
>>> y = {'a': 1}
>>> dict(x.items() | y.items())
{'a': 2}

您不应该使用的另一种技巧:

z = dict(x, **y)

这使用了dict构造函数,并且非常快速且具有内存效率(甚至比我们的两步过程略高),但是除非您确切地知道这里正在发生什么(也就是说,第二个dict作为关键字参数传递给dict,构造函数),很难阅读,它不是预期的用法,因此不是Pythonic。

这是在django修复的用法示例。

字典旨在获取可散列的键(例如,frozenset或元组),但是当键不是字符串时此方法在Python 3中失败。

>>> c = dict(a, **b)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: keyword arguments must be strings

邮件列表中,该语言的创建者Guido van Rossum写道:

我宣布dict({},** {1:3})非法是可以的,因为毕竟这是对**机制的滥用。

显然dict(x,** y)被“调用x.update(y)并返回x”的“酷砍”。我个人觉得它比酷更卑鄙。

根据我的理解(以及对语言创建者的理解),该命令的预期用途dict(**y)是用于创建可读性强的命令,例如:

dict(a=1, b=10, c=11)

代替

{'a': 1, 'b': 10, 'c': 11}

对评论的回应

尽管Guido说了什么dict(x, **y),但符合dict规范,顺便说一句。它仅适用于Python 2和3。事实上,这仅适用于字符串键,这是关键字参数如何工作的直接结果,而不是字典的简称。在这个地方使用**运算符也不会滥用该机制,实际上**正是为了将dict作为关键字传递而设计的。

同样,当键为非字符串时,它不适用于3。隐式调用协定是命名空间采用普通命令,而用户只能传递字符串形式的关键字参数。所有其他可调用对象都强制执行了它。dict在Python 2中破坏了这种一致性:

>>> foo(**{('a', 'b'): None})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: foo() keywords must be strings
>>> dict(**{('a', 'b'): None})
{('a', 'b'): None}

考虑到其他Python实现(Pypy,Jython,IronPython),这种不一致是很糟糕的。因此,它在Python 3中已得到修复,因为这种用法可能是一个重大更改。

我向您指出,故意编写仅在一种语言版本中有效或仅在特定的任意约束下有效的代码是恶意的无能。

更多评论:

dict(x.items() + y.items()) 仍然是Python 2最具可读性的解决方案。可读性至关重要。

我的回答:merge_two_dicts(x, y)如果我们实际上担心可读性,实际上对我来说似乎更加清晰。而且它不向前兼容,因为Python 2越来越不推荐使用。

{**x, **y}似乎不处理嵌套字典。嵌套键的内容只是被覆盖,而不是被合并。我最终被这些没有递归合并的答案所烧死,令我惊讶的是,没有人提到它。在我对“合并”一词的解释中,这些答案描述的是“将一个词典与另一个词典更新”,而不是合并。

是。我必须回头再问这个问题,它要求两个字典进行浅层合并,第一个字典的值由第二个字典覆盖-在一个表达式中。

假设有两个字典,一个字典可能会递归地将它们合并到一个函数中,但是您应注意不要从任何一个源修改字典,避免这种情况的最可靠方法是在分配值时进行复制。由于键必须是可散列的,因此通常是不可变的,因此复制它们毫无意义:

from copy import deepcopy

def dict_of_dicts_merge(x, y):
    z = {}
    overlapping_keys = x.keys() & y.keys()
    for key in overlapping_keys:
        z[key] = dict_of_dicts_merge(x[key], y[key])
    for key in x.keys() - overlapping_keys:
        z[key] = deepcopy(x[key])
    for key in y.keys() - overlapping_keys:
        z[key] = deepcopy(y[key])
    return z

用法:

>>> x = {'a':{1:{}}, 'b': {2:{}}}
>>> y = {'b':{10:{}}, 'c': {11:{}}}
>>> dict_of_dicts_merge(x, y)
{'b': {2: {}, 10: {}}, 'a': {1: {}}, 'c': {11: {}}}

提出其他值类型的偶发性问题远远超出了此问题的范围,因此,我将为您回答有关“词典合并字典”的规范问题的答案

性能较差但临时性正确

这些方法的性能较差,但是它们将提供正确的行为。他们将少得多比高性能copyupdate或新的拆包,因为他们通过在更高的抽象水平的每个键-值对迭代,但他们做的尊重优先顺序(后者类型的字典具有优先权)

您还可以在dict理解内手动将dict链接:

{k: v for d in dicts for k, v in d.items()} # iteritems in Python 2.7

或在python 2.6中(也许在引入生成器表达式时早在2.4中):

dict((k, v) for d in dicts for k, v in d.items())

itertools.chain 将以正确的顺序在键值对上链接迭代器:

import itertools
z = dict(itertools.chain(x.iteritems(), y.iteritems()))

绩效分析

我将仅对已知行为正确的用法进行性能分析。

import timeit

在Ubuntu 14.04上完成以下操作

在Python 2.7(系统Python)中:

>>> min(timeit.repeat(lambda: merge_two_dicts(x, y)))
0.5726828575134277
>>> min(timeit.repeat(lambda: {k: v for d in (x, y) for k, v in d.items()} ))
1.163769006729126
>>> min(timeit.repeat(lambda: dict(itertools.chain(x.iteritems(), y.iteritems()))))
1.1614501476287842
>>> min(timeit.repeat(lambda: dict((k, v) for d in (x, y) for k, v in d.items())))
2.2345519065856934

在Python 3.5中(死神PPA):

>>> min(timeit.repeat(lambda: {**x, **y}))
0.4094954460160807
>>> min(timeit.repeat(lambda: merge_two_dicts(x, y)))
0.7881555100320838
>>> min(timeit.repeat(lambda: {k: v for d in (x, y) for k, v in d.items()} ))
1.4525277839857154
>>> min(timeit.repeat(lambda: dict(itertools.chain(x.items(), y.items()))))
2.3143140770262107
>>> min(timeit.repeat(lambda: dict((k, v) for d in (x, y) for k, v in d.items())))
3.2069112799945287

词典资源

How can I merge two Python dictionaries in a single expression?

For dictionaries x and y, z becomes a shallowly merged dictionary with values from y replacing those from x.

  • In Python 3.5 or greater:

    z = {**x, **y}
    
  • In Python 2, (or 3.4 or lower) write a function:

    def merge_two_dicts(x, y):
        z = x.copy()   # start with x's keys and values
        z.update(y)    # modifies z with y's keys and values & returns None
        return z
    

    and now:

    z = merge_two_dicts(x, y)
    
  • In Python 3.9.0a4 or greater (final release date approx October 2020): PEP-584, discussed here, was implemented to further simplify this:

    z = x | y          # NOTE: 3.9+ ONLY
    

Explanation

Say you have two dicts and you want to merge them into a new dict without altering the original dicts:

x = {'a': 1, 'b': 2}
y = {'b': 3, 'c': 4}

The desired result is to get a new dictionary (z) with the values merged, and the second dict’s values overwriting those from the first.

>>> z
{'a': 1, 'b': 3, 'c': 4}

A new syntax for this, proposed in PEP 448 and available as of Python 3.5, is

z = {**x, **y}

And it is indeed a single expression.

Note that we can merge in with literal notation as well:

z = {**x, 'foo': 1, 'bar': 2, **y}

and now:

>>> z
{'a': 1, 'b': 3, 'foo': 1, 'bar': 2, 'c': 4}

It is now showing as implemented in the release schedule for 3.5, PEP 478, and it has now made its way into What’s New in Python 3.5 document.

However, since many organizations are still on Python 2, you may wish to do this in a backwards compatible way. The classically Pythonic way, available in Python 2 and Python 3.0-3.4, is to do this as a two-step process:

z = x.copy()
z.update(y) # which returns None since it mutates z

In both approaches, y will come second and its values will replace x‘s values, thus 'b' will point to 3 in our final result.

Not yet on Python 3.5, but want a single expression

If you are not yet on Python 3.5, or need to write backward-compatible code, and you want this in a single expression, the most performant while correct approach is to put it in a function:

def merge_two_dicts(x, y):
    """Given two dicts, merge them into a new dict as a shallow copy."""
    z = x.copy()
    z.update(y)
    return z

and then you have a single expression:

z = merge_two_dicts(x, y)

You can also make a function to merge an undefined number of dicts, from zero to a very large number:

def merge_dicts(*dict_args):
    """
    Given any number of dicts, shallow copy and merge into a new dict,
    precedence goes to key value pairs in latter dicts.
    """
    result = {}
    for dictionary in dict_args:
        result.update(dictionary)
    return result

This function will work in Python 2 and 3 for all dicts. e.g. given dicts a to g:

z = merge_dicts(a, b, c, d, e, f, g) 

and key value pairs in g will take precedence over dicts a to f, and so on.

Critiques of Other Answers

Don’t use what you see in the formerly accepted answer:

z = dict(x.items() + y.items())

In Python 2, you create two lists in memory for each dict, create a third list in memory with length equal to the length of the first two put together, and then discard all three lists to create the dict. In Python 3, this will fail because you’re adding two dict_items objects together, not two lists –

>>> c = dict(a.items() + b.items())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for +: 'dict_items' and 'dict_items'

and you would have to explicitly create them as lists, e.g. z = dict(list(x.items()) + list(y.items())). This is a waste of resources and computation power.

Similarly, taking the union of items() in Python 3 (viewitems() in Python 2.7) will also fail when values are unhashable objects (like lists, for example). Even if your values are hashable, since sets are semantically unordered, the behavior is undefined in regards to precedence. So don’t do this:

>>> c = dict(a.items() | b.items())

This example demonstrates what happens when values are unhashable:

>>> x = {'a': []}
>>> y = {'b': []}
>>> dict(x.items() | y.items())
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'

Here’s an example where y should have precedence, but instead the value from x is retained due to the arbitrary order of sets:

>>> x = {'a': 2}
>>> y = {'a': 1}
>>> dict(x.items() | y.items())
{'a': 2}

Another hack you should not use:

z = dict(x, **y)

This uses the dict constructor, and is very fast and memory efficient (even slightly more-so than our two-step process) but unless you know precisely what is happening here (that is, the second dict is being passed as keyword arguments to the dict constructor), it’s difficult to read, it’s not the intended usage, and so it is not Pythonic.

Here’s an example of the usage being remediated in django.

Dicts are intended to take hashable keys (e.g. frozensets or tuples), but this method fails in Python 3 when keys are not strings.

>>> c = dict(a, **b)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: keyword arguments must be strings

From the mailing list, Guido van Rossum, the creator of the language, wrote:

I am fine with declaring dict({}, **{1:3}) illegal, since after all it is abuse of the ** mechanism.

and

Apparently dict(x, **y) is going around as “cool hack” for “call x.update(y) and return x”. Personally I find it more despicable than cool.

It is my understanding (as well as the understanding of the creator of the language) that the intended usage for dict(**y) is for creating dicts for readability purposes, e.g.:

dict(a=1, b=10, c=11)

instead of

{'a': 1, 'b': 10, 'c': 11}

Response to comments

Despite what Guido says, dict(x, **y) is in line with the dict specification, which btw. works for both Python 2 and 3. The fact that this only works for string keys is a direct consequence of how keyword parameters work and not a short-comming of dict. Nor is using the ** operator in this place an abuse of the mechanism, in fact ** was designed precisely to pass dicts as keywords.

Again, it doesn’t work for 3 when keys are non-strings. The implicit calling contract is that namespaces take ordinary dicts, while users must only pass keyword arguments that are strings. All other callables enforced it. dict broke this consistency in Python 2:

>>> foo(**{('a', 'b'): None})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: foo() keywords must be strings
>>> dict(**{('a', 'b'): None})
{('a', 'b'): None}

This inconsistency was bad given other implementations of Python (Pypy, Jython, IronPython). Thus it was fixed in Python 3, as this usage could be a breaking change.

I submit to you that it is malicious incompetence to intentionally write code that only works in one version of a language or that only works given certain arbitrary constraints.

More comments:

dict(x.items() + y.items()) is still the most readable solution for Python 2. Readability counts.

My response: merge_two_dicts(x, y) actually seems much clearer to me, if we’re actually concerned about readability. And it is not forward compatible, as Python 2 is increasingly deprecated.

{**x, **y} does not seem to handle nested dictionaries. the contents of nested keys are simply overwritten, not merged […] I ended up being burnt by these answers that do not merge recursively and I was surprised no one mentioned it. In my interpretation of the word “merging” these answers describe “updating one dict with another”, and not merging.

Yes. I must refer you back to the question, which is asking for a shallow merge of two dictionaries, with the first’s values being overwritten by the second’s – in a single expression.

Assuming two dictionary of dictionaries, one might recursively merge them in a single function, but you should be careful not to modify the dicts from either source, and the surest way to avoid that is to make a copy when assigning values. As keys must be hashable and are usually therefore immutable, it is pointless to copy them:

from copy import deepcopy

def dict_of_dicts_merge(x, y):
    z = {}
    overlapping_keys = x.keys() & y.keys()
    for key in overlapping_keys:
        z[key] = dict_of_dicts_merge(x[key], y[key])
    for key in x.keys() - overlapping_keys:
        z[key] = deepcopy(x[key])
    for key in y.keys() - overlapping_keys:
        z[key] = deepcopy(y[key])
    return z

Usage:

>>> x = {'a':{1:{}}, 'b': {2:{}}}
>>> y = {'b':{10:{}}, 'c': {11:{}}}
>>> dict_of_dicts_merge(x, y)
{'b': {2: {}, 10: {}}, 'a': {1: {}}, 'c': {11: {}}}

Coming up with contingencies for other value types is far beyond the scope of this question, so I will point you at my answer to the canonical question on a “Dictionaries of dictionaries merge”.

Less Performant But Correct Ad-hocs

These approaches are less performant, but they will provide correct behavior. They will be much less performant than copy and update or the new unpacking because they iterate through each key-value pair at a higher level of abstraction, but they do respect the order of precedence (latter dicts have precedence)

You can also chain the dicts manually inside a dict comprehension:

{k: v for d in dicts for k, v in d.items()} # iteritems in Python 2.7

or in python 2.6 (and perhaps as early as 2.4 when generator expressions were introduced):

dict((k, v) for d in dicts for k, v in d.items())

itertools.chain will chain the iterators over the key-value pairs in the correct order:

import itertools
z = dict(itertools.chain(x.iteritems(), y.iteritems()))

Performance Analysis

I’m only going to do the performance analysis of the usages known to behave correctly.

import timeit

The following is done on Ubuntu 14.04

In Python 2.7 (system Python):

>>> min(timeit.repeat(lambda: merge_two_dicts(x, y)))
0.5726828575134277
>>> min(timeit.repeat(lambda: {k: v for d in (x, y) for k, v in d.items()} ))
1.163769006729126
>>> min(timeit.repeat(lambda: dict(itertools.chain(x.iteritems(), y.iteritems()))))
1.1614501476287842
>>> min(timeit.repeat(lambda: dict((k, v) for d in (x, y) for k, v in d.items())))
2.2345519065856934

In Python 3.5 (deadsnakes PPA):

>>> min(timeit.repeat(lambda: {**x, **y}))
0.4094954460160807
>>> min(timeit.repeat(lambda: merge_two_dicts(x, y)))
0.7881555100320838
>>> min(timeit.repeat(lambda: {k: v for d in (x, y) for k, v in d.items()} ))
1.4525277839857154
>>> min(timeit.repeat(lambda: dict(itertools.chain(x.items(), y.items()))))
2.3143140770262107
>>> min(timeit.repeat(lambda: dict((k, v) for d in (x, y) for k, v in d.items())))
3.2069112799945287

Resources on Dictionaries


回答 1

您的情况是:

z = dict(x.items() + y.items())

可以根据需要将最终的dict放入中z,并使key的值b被第二(y)dict的值正确覆盖:

>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> z = dict(x.items() + y.items())
>>> z
{'a': 1, 'c': 11, 'b': 10}

如果您使用Python 3,它只会稍微复杂一点。创建z

>>> z = dict(list(x.items()) + list(y.items()))
>>> z
{'a': 1, 'c': 11, 'b': 10}

如果您使用Python版本3.9.0a4或更高版本,则可以直接使用:

x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
z = x | y
print(z)

Output: {'a': 1, 'c': 11, 'b': 10}

In your case, what you can do is:

z = dict(x.items() + y.items())

This will, as you want it, put the final dict in z, and make the value for key b be properly overridden by the second (y) dict’s value:

>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> z = dict(x.items() + y.items())
>>> z
{'a': 1, 'c': 11, 'b': 10}

If you use Python 3, it is only a little more complicated. To create z:

>>> z = dict(list(x.items()) + list(y.items()))
>>> z
{'a': 1, 'c': 11, 'b': 10}

If you use Python version 3.9.0a4 or greater, then you can directly use:

x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
z = x | y
print(z)

Output: {'a': 1, 'c': 11, 'b': 10}

回答 2

替代:

z = x.copy()
z.update(y)

An alternative:

z = x.copy()
z.update(y)

回答 3

另一个更简洁的选择:

z = dict(x, **y)

注意:这已经成为一个流行的答案,但必须指出的是,如果y有任何非字符串键,那么它实际上是对CPython实现细节的滥用,并且在Python 3中不起作用,或在PyPy,IronPython或Jython中。另外,Guido也不是粉丝。因此,我不建议将此技术用于前向兼容或交叉实现的可移植代码,这实际上意味着应完全避免使用它。

Another, more concise, option:

z = dict(x, **y)

Note: this has become a popular answer, but it is important to point out that if y has any non-string keys, the fact that this works at all is an abuse of a CPython implementation detail, and it does not work in Python 3, or in PyPy, IronPython, or Jython. Also, Guido is not a fan. So I can’t recommend this technique for forward-compatible or cross-implementation portable code, which really means it should be avoided entirely.


回答 4

这可能不是一个流行的答案,但是您几乎可以肯定不想这样做。如果要合并的副本,请使用copy(或deepcopy,具体取决于您的需求),然后进行更新。与使用.items()+ .items()进行单行创建相比,两行代码更具可读性-更具Python风格。显式胜于隐式。

此外,当您使用.items()(Python 3.0之前的版本)时,您正在创建一个新列表,其中包含字典中的项目。如果您的词典很大,那将是很大的开销(创建合并字典后将立即丢弃两个大列表)。update()可以更高效地工作,因为它可以逐项执行第二个字典。

时间方面

>>> timeit.Timer("dict(x, **y)", "x = dict(zip(range(1000), range(1000)))\ny=dict(zip(range(1000,2000), range(1000,2000)))").timeit(100000)
15.52571702003479
>>> timeit.Timer("temp = x.copy()\ntemp.update(y)", "x = dict(zip(range(1000), range(1000)))\ny=dict(zip(range(1000,2000), range(1000,2000)))").timeit(100000)
15.694622993469238
>>> timeit.Timer("dict(x.items() + y.items())", "x = dict(zip(range(1000), range(1000)))\ny=dict(zip(range(1000,2000), range(1000,2000)))").timeit(100000)
41.484580039978027

IMO出于可读性考虑,前两者之间的微小速度下降是值得的。此外,仅在Python 2.3中添加了用于字典创建的关键字参数,而copy()和update()将在较早的版本中运行。

This probably won’t be a popular answer, but you almost certainly do not want to do this. If you want a copy that’s a merge, then use copy (or deepcopy, depending on what you want) and then update. The two lines of code are much more readable – more Pythonic – than the single line creation with .items() + .items(). Explicit is better than implicit.

In addition, when you use .items() (pre Python 3.0), you’re creating a new list that contains the items from the dict. If your dictionaries are large, then that is quite a lot of overhead (two large lists that will be thrown away as soon as the merged dict is created). update() can work more efficiently, because it can run through the second dict item-by-item.

In terms of time:

>>> timeit.Timer("dict(x, **y)", "x = dict(zip(range(1000), range(1000)))\ny=dict(zip(range(1000,2000), range(1000,2000)))").timeit(100000)
15.52571702003479
>>> timeit.Timer("temp = x.copy()\ntemp.update(y)", "x = dict(zip(range(1000), range(1000)))\ny=dict(zip(range(1000,2000), range(1000,2000)))").timeit(100000)
15.694622993469238
>>> timeit.Timer("dict(x.items() + y.items())", "x = dict(zip(range(1000), range(1000)))\ny=dict(zip(range(1000,2000), range(1000,2000)))").timeit(100000)
41.484580039978027

IMO the tiny slowdown between the first two is worth it for the readability. In addition, keyword arguments for dictionary creation was only added in Python 2.3, whereas copy() and update() will work in older versions.


回答 5

在后续回答中,您询问了这两种选择的相对性能:

z1 = dict(x.items() + y.items())
z2 = dict(x, **y)

至少在我的机器上(运行Python 2.5.2的相当普通的x86_64),替代z2方法不仅更短,更简单,而且显着更快。您可以使用timeitPython随附的模块自行验证。

示例1:相同的字典将20个连续的整数映射到自身:

% python -m timeit -s 'x=y=dict((i,i) for i in range(20))' 'z1=dict(x.items() + y.items())'
100000 loops, best of 3: 5.67 usec per loop
% python -m timeit -s 'x=y=dict((i,i) for i in range(20))' 'z2=dict(x, **y)' 
100000 loops, best of 3: 1.53 usec per loop

z2胜出率约为3.5。不同的词典似乎会产生完全不同的结果,但z2似乎总是遥遥领先。(如果同一测试的结果不一致,请尝试传递-r大于默认值3的数字。)

示例2:非重叠字典将252个短字符串映射为整数,反之亦然:

% python -m timeit -s 'from htmlentitydefs import codepoint2name as x, name2codepoint as y' 'z1=dict(x.items() + y.items())'
1000 loops, best of 3: 260 usec per loop
% python -m timeit -s 'from htmlentitydefs import codepoint2name as x, name2codepoint as y' 'z2=dict(x, **y)'               
10000 loops, best of 3: 26.9 usec per loop

z2 赢了大约10倍。这是我书中相当大的胜利!

比较z1完这两个项目后,我想知道的不佳表现是否可以归因于构建两个项目列表的开销,这反过来又让我想知道这种变化是否会更好:

from itertools import chain
z3 = dict(chain(x.iteritems(), y.iteritems()))

一些快速测试,例如

% python -m timeit -s 'from itertools import chain; from htmlentitydefs import codepoint2name as x, name2codepoint as y' 'z3=dict(chain(x.iteritems(), y.iteritems()))'
10000 loops, best of 3: 66 usec per loop

我得出的结论是,z3它的速度要比速度快z1,但不及速度z2。绝对不值得所有额外的打字。

讨论中仍然缺少一些重要的内容,这是将这些替代方法与合并两个列表的“明显”方法的性能比较:使用该update方法。为了使事情与表达式保持一致,而不会修改x或y,我将制作x的副本,而不是就地对其进行修改,如下所示:

z0 = dict(x)
z0.update(y)

典型结果:

% python -m timeit -s 'from htmlentitydefs import codepoint2name as x, name2codepoint as y' 'z0=dict(x); z0.update(y)'
10000 loops, best of 3: 26.9 usec per loop

换句话说,z0并且z2似乎具有基本相同的性能。您认为这可能是巧合吗?我不….

实际上,我什至声称纯粹的Python代码不可能做到比这更好。而且,如果您可以在C扩展模块中做得更好,我想Python人士可能会对将您的代码(或方法的变体)并入Python核心感兴趣。Python dict在很多地方都使用过;优化运营非常重要。

您也可以这样写

z0 = x.copy()
z0.update(y)

就像Tony一样,但是(并不奇怪)表示法上的差异对性能没有任何可测量的影响。使用对您而言合适的任何一种。当然,他绝对正确地指出,两语句版本更容易理解。

In a follow-up answer, you asked about the relative performance of these two alternatives:

z1 = dict(x.items() + y.items())
z2 = dict(x, **y)

On my machine, at least (a fairly ordinary x86_64 running Python 2.5.2), alternative z2 is not only shorter and simpler but also significantly faster. You can verify this for yourself using the timeit module that comes with Python.

Example 1: identical dictionaries mapping 20 consecutive integers to themselves:

% python -m timeit -s 'x=y=dict((i,i) for i in range(20))' 'z1=dict(x.items() + y.items())'
100000 loops, best of 3: 5.67 usec per loop
% python -m timeit -s 'x=y=dict((i,i) for i in range(20))' 'z2=dict(x, **y)' 
100000 loops, best of 3: 1.53 usec per loop

z2 wins by a factor of 3.5 or so. Different dictionaries seem to yield quite different results, but z2 always seems to come out ahead. (If you get inconsistent results for the same test, try passing in -r with a number larger than the default 3.)

Example 2: non-overlapping dictionaries mapping 252 short strings to integers and vice versa:

% python -m timeit -s 'from htmlentitydefs import codepoint2name as x, name2codepoint as y' 'z1=dict(x.items() + y.items())'
1000 loops, best of 3: 260 usec per loop
% python -m timeit -s 'from htmlentitydefs import codepoint2name as x, name2codepoint as y' 'z2=dict(x, **y)'               
10000 loops, best of 3: 26.9 usec per loop

z2 wins by about a factor of 10. That’s a pretty big win in my book!

After comparing those two, I wondered if z1‘s poor performance could be attributed to the overhead of constructing the two item lists, which in turn led me to wonder if this variation might work better:

from itertools import chain
z3 = dict(chain(x.iteritems(), y.iteritems()))

A few quick tests, e.g.

% python -m timeit -s 'from itertools import chain; from htmlentitydefs import codepoint2name as x, name2codepoint as y' 'z3=dict(chain(x.iteritems(), y.iteritems()))'
10000 loops, best of 3: 66 usec per loop

lead me to conclude that z3 is somewhat faster than z1, but not nearly as fast as z2. Definitely not worth all the extra typing.

This discussion is still missing something important, which is a performance comparison of these alternatives with the “obvious” way of merging two lists: using the update method. To try to keep things on an equal footing with the expressions, none of which modify x or y, I’m going to make a copy of x instead of modifying it in-place, as follows:

z0 = dict(x)
z0.update(y)

A typical result:

% python -m timeit -s 'from htmlentitydefs import codepoint2name as x, name2codepoint as y' 'z0=dict(x); z0.update(y)'
10000 loops, best of 3: 26.9 usec per loop

In other words, z0 and z2 seem to have essentially identical performance. Do you think this might be a coincidence? I don’t….

In fact, I’d go so far as to claim that it’s impossible for pure Python code to do any better than this. And if you can do significantly better in a C extension module, I imagine the Python folks might well be interested in incorporating your code (or a variation on your approach) into the Python core. Python uses dict in lots of places; optimizing its operations is a big deal.

You could also write this as

z0 = x.copy()
z0.update(y)

as Tony does, but (not surprisingly) the difference in notation turns out not to have any measurable effect on performance. Use whichever looks right to you. Of course, he’s absolutely correct to point out that the two-statement version is much easier to understand.


回答 6

在Python 3.0及更高版本中,您可以使用collections.ChainMap将多个字典或其他映射组合在一起的方式来创建一个可更新的视图:

>>> from collections import ChainMap
>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> z = dict(ChainMap({}, y, x))
>>> for k, v in z.items():
        print(k, '-->', v)

a --> 1
b --> 10
c --> 11

适用于Python 3.5和更高版本的更新:可以使用PEP 448扩展词典打包和拆包。快速简便:

>>> x = {'a':1, 'b': 2}
>>> y = y = {'b':10, 'c': 11}
>>> {**x, **y}
{'a': 1, 'b': 10, 'c': 11}

In Python 3.0 and later, you can use collections.ChainMap which groups multiple dicts or other mappings together to create a single, updateable view:

>>> from collections import ChainMap
>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> z = dict(ChainMap({}, y, x))
>>> for k, v in z.items():
        print(k, '-->', v)

a --> 1
b --> 10
c --> 11

Update for Python 3.5 and later: You can use PEP 448 extended dictionary packing and unpacking. This is fast and easy:

>>> x = {'a':1, 'b': 2}
>>> y = y = {'b':10, 'c': 11}
>>> {**x, **y}
{'a': 1, 'b': 10, 'c': 11}

回答 7

我想要类似的东西,但是能够指定如何合并重复键上的值,所以我破解了这个(但并未对其进行大量测试)。显然,这不是单个表达式,而是单个函数调用。

def merge(d1, d2, merge_fn=lambda x,y:y):
    """
    Merges two dictionaries, non-destructively, combining 
    values on duplicate keys as defined by the optional merge
    function.  The default behavior replaces the values in d1
    with corresponding values in d2.  (There is no other generally
    applicable merge strategy, but often you'll have homogeneous 
    types in your dicts, so specifying a merge technique can be 
    valuable.)

    Examples:

    >>> d1
    {'a': 1, 'c': 3, 'b': 2}
    >>> merge(d1, d1)
    {'a': 1, 'c': 3, 'b': 2}
    >>> merge(d1, d1, lambda x,y: x+y)
    {'a': 2, 'c': 6, 'b': 4}

    """
    result = dict(d1)
    for k,v in d2.iteritems():
        if k in result:
            result[k] = merge_fn(result[k], v)
        else:
            result[k] = v
    return result

I wanted something similar, but with the ability to specify how the values on duplicate keys were merged, so I hacked this out (but did not heavily test it). Obviously this is not a single expression, but it is a single function call.

def merge(d1, d2, merge_fn=lambda x,y:y):
    """
    Merges two dictionaries, non-destructively, combining 
    values on duplicate keys as defined by the optional merge
    function.  The default behavior replaces the values in d1
    with corresponding values in d2.  (There is no other generally
    applicable merge strategy, but often you'll have homogeneous 
    types in your dicts, so specifying a merge technique can be 
    valuable.)

    Examples:

    >>> d1
    {'a': 1, 'c': 3, 'b': 2}
    >>> merge(d1, d1)
    {'a': 1, 'c': 3, 'b': 2}
    >>> merge(d1, d1, lambda x,y: x+y)
    {'a': 2, 'c': 6, 'b': 4}

    """
    result = dict(d1)
    for k,v in d2.iteritems():
        if k in result:
            result[k] = merge_fn(result[k], v)
        else:
            result[k] = v
    return result

回答 8

递归/深度更新字典

def deepupdate(original, update):
    """
    Recursively update a dict.
    Subdict's won't be overwritten but also updated.
    """
    for key, value in original.iteritems(): 
        if key not in update:
            update[key] = value
        elif isinstance(value, dict):
            deepupdate(value, update[key]) 
    return update

示范:

pluto_original = {
    'name': 'Pluto',
    'details': {
        'tail': True,
        'color': 'orange'
    }
}

pluto_update = {
    'name': 'Pluutoo',
    'details': {
        'color': 'blue'
    }
}

print deepupdate(pluto_original, pluto_update)

输出:

{
    'name': 'Pluutoo',
    'details': {
        'color': 'blue',
        'tail': True
    }
}

感谢rednaw的编辑。

Recursively/deep update a dict

def deepupdate(original, update):
    """
    Recursively update a dict.
    Subdict's won't be overwritten but also updated.
    """
    for key, value in original.iteritems(): 
        if key not in update:
            update[key] = value
        elif isinstance(value, dict):
            deepupdate(value, update[key]) 
    return update

Demonstration:

pluto_original = {
    'name': 'Pluto',
    'details': {
        'tail': True,
        'color': 'orange'
    }
}

pluto_update = {
    'name': 'Pluutoo',
    'details': {
        'color': 'blue'
    }
}

print deepupdate(pluto_original, pluto_update)

Outputs:

{
    'name': 'Pluutoo',
    'details': {
        'color': 'blue',
        'tail': True
    }
}

Thanks rednaw for edits.


回答 9

我不使用副本时可能想到的最佳版本是:

from itertools import chain
x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
dict(chain(x.iteritems(), y.iteritems()))

至少在CPython上,它比快,dict(x.items() + y.items())但没有快n = copy(a); n.update(b)。如果更改iteritems()items(),则此版本在Python 3中也可以使用,这是2to3工具自动完成的。

我个人最喜欢这个版本,因为它用一种功能语法很好地描述了我想要的内容。唯一的小问题是,来自y的值优先于来自x的值并不能完全清楚,但是我不认为很难弄清楚。

The best version I could think while not using copy would be:

from itertools import chain
x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
dict(chain(x.iteritems(), y.iteritems()))

It’s faster than dict(x.items() + y.items()) but not as fast as n = copy(a); n.update(b), at least on CPython. This version also works in Python 3 if you change iteritems() to items(), which is automatically done by the 2to3 tool.

Personally I like this version best because it describes fairly good what I want in a single functional syntax. The only minor problem is that it doesn’t make completely obvious that values from y takes precedence over values from x, but I don’t believe it’s difficult to figure that out.


回答 10

Python 3.5(PEP 448)允许使用更好的语法选项:

x = {'a': 1, 'b': 1}
y = {'a': 2, 'c': 2}
final = {**x, **y} 
final
# {'a': 2, 'b': 1, 'c': 2}

甚至

final = {'a': 1, 'b': 1, **x, **y}

在Python 3.9中,您还可以使用| 和| =以及下面来自PEP 584的示例

d = {'spam': 1, 'eggs': 2, 'cheese': 3}
e = {'cheese': 'cheddar', 'aardvark': 'Ethel'}
d | e
# {'spam': 1, 'eggs': 2, 'cheese': 'cheddar', 'aardvark': 'Ethel'}

Python 3.5 (PEP 448) allows a nicer syntax option:

x = {'a': 1, 'b': 1}
y = {'a': 2, 'c': 2}
final = {**x, **y} 
final
# {'a': 2, 'b': 1, 'c': 2}

Or even

final = {'a': 1, 'b': 1, **x, **y}

In Python 3.9 you also use | and |= with the below example from PEP 584

d = {'spam': 1, 'eggs': 2, 'cheese': 3}
e = {'cheese': 'cheddar', 'aardvark': 'Ethel'}
d | e
# {'spam': 1, 'eggs': 2, 'cheese': 'cheddar', 'aardvark': 'Ethel'}

回答 11

x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
z = dict(x.items() + y.items())
print z

对于两个字典中都有键的项目,您可以通过将最后一个放在输出中来控制哪一个在输出中结束。

x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
z = dict(x.items() + y.items())
print z

For items with keys in both dictionaries (‘b’), you can control which one ends up in the output by putting that one last.


回答 12

虽然已经多次回答了该问题,但尚未列出此问题的简单解决方案。

x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
z4 = {}
z4.update(x)
z4.update(y)

它与上面提到的z0和邪恶z2一样快,但易于理解和更改。

While the question has already been answered several times, this simple solution to the problem has not been listed yet.

x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
z4 = {}
z4.update(x)
z4.update(y)

It is as fast as z0 and the evil z2 mentioned above, but easy to understand and change.


回答 13

def dict_merge(a, b):
  c = a.copy()
  c.update(b)
  return c

new = dict_merge(old, extras)

在这些阴暗而可疑的答案中,这个出色的例子是在Python中合并字典的唯一且唯一的好方法,这是独裁者终身支持的Guido van Rossum本人!有人提出了一半的建议,但没有将其放在函数中。

print dict_merge(
      {'color':'red', 'model':'Mini'},
      {'model':'Ferrari', 'owner':'Carl'})

给出:

{'color': 'red', 'owner': 'Carl', 'model': 'Ferrari'}
def dict_merge(a, b):
  c = a.copy()
  c.update(b)
  return c

new = dict_merge(old, extras)

Among such shady and dubious answers, this shining example is the one and only good way to merge dicts in Python, endorsed by dictator for life Guido van Rossum himself! Someone else suggested half of this, but did not put it in a function.

print dict_merge(
      {'color':'red', 'model':'Mini'},
      {'model':'Ferrari', 'owner':'Carl'})

gives:

{'color': 'red', 'owner': 'Carl', 'model': 'Ferrari'}

回答 14

如果您认为lambda是邪恶的,那么请继续阅读。根据要求,您可以使用一个表达式编写快速而高效的内存解决方案:

x = {'a':1, 'b':2}
y = {'b':10, 'c':11}
z = (lambda a, b: (lambda a_copy: a_copy.update(b) or a_copy)(a.copy()))(x, y)
print z
{'a': 1, 'c': 11, 'b': 10}
print x
{'a': 1, 'b': 2}

如上所述,使用两行或编写函数可能是更好的方法。

If you think lambdas are evil then read no further. As requested, you can write the fast and memory-efficient solution with one expression:

x = {'a':1, 'b':2}
y = {'b':10, 'c':11}
z = (lambda a, b: (lambda a_copy: a_copy.update(b) or a_copy)(a.copy()))(x, y)
print z
{'a': 1, 'c': 11, 'b': 10}
print x
{'a': 1, 'b': 2}

As suggested above, using two lines or writing a function is probably a better way to go.


回答 15

是pythonic。使用理解

z={i:d[i] for d in [x,y] for i in d}

>>> print z
{'a': 1, 'c': 11, 'b': 10}

Be pythonic. Use a comprehension:

z={i:d[i] for d in [x,y] for i in d}

>>> print z
{'a': 1, 'c': 11, 'b': 10}

回答 16

在python3中,该items方法不再返回list,而是返回一个view,其作用类似于set。在这种情况下,您将需要使用set联合,因为与的连接+将不起作用:

dict(x.items() | y.items())

对于2.7版中的类似python3的行为,该viewitems方法应代替items

dict(x.viewitems() | y.viewitems())

无论如何,我还是更喜欢这种表示法,因为将其视为固定的联合运算而不是串联(如标题所示)似乎更为自然。

编辑:

对于python 3还有几点。首先,请注意,dict(x, **y)除非输入的键y是字符串,否则该技巧在python 3中将不起作用。

而且,Raymond Hettinger的Chainmap 答案非常优雅,因为它可以将任意数量的dicts作为参数,但是从文档中看,它似乎依次遍历了每次查找的所有dicts列表:

查找顺序搜索基础映射,直到找到密钥为止。

如果您的应用程序中有很多查找,这可能会减慢您的速度:

In [1]: from collections import ChainMap
In [2]: from string import ascii_uppercase as up, ascii_lowercase as lo; x = dict(zip(lo, up)); y = dict(zip(up, lo))
In [3]: chainmap_dict = ChainMap(y, x)
In [4]: union_dict = dict(x.items() | y.items())
In [5]: timeit for k in union_dict: union_dict[k]
100000 loops, best of 3: 2.15 µs per loop
In [6]: timeit for k in chainmap_dict: chainmap_dict[k]
10000 loops, best of 3: 27.1 µs per loop

因此,查找速度要慢一个数量级。我是Chainmap的粉丝,但在可能有很多查找的地方看起来不太实用。

In python3, the items method no longer returns a list, but rather a view, which acts like a set. In this case you’ll need to take the set union since concatenating with + won’t work:

dict(x.items() | y.items())

For python3-like behavior in version 2.7, the viewitems method should work in place of items:

dict(x.viewitems() | y.viewitems())

I prefer this notation anyways since it seems more natural to think of it as a set union operation rather than concatenation (as the title shows).

Edit:

A couple more points for python 3. First, note that the dict(x, **y) trick won’t work in python 3 unless the keys in y are strings.

Also, Raymond Hettinger’s Chainmap answer is pretty elegant, since it can take an arbitrary number of dicts as arguments, but from the docs it looks like it sequentially looks through a list of all the dicts for each lookup:

Lookups search the underlying mappings successively until a key is found.

This can slow you down if you have a lot of lookups in your application:

In [1]: from collections import ChainMap
In [2]: from string import ascii_uppercase as up, ascii_lowercase as lo; x = dict(zip(lo, up)); y = dict(zip(up, lo))
In [3]: chainmap_dict = ChainMap(y, x)
In [4]: union_dict = dict(x.items() | y.items())
In [5]: timeit for k in union_dict: union_dict[k]
100000 loops, best of 3: 2.15 µs per loop
In [6]: timeit for k in chainmap_dict: chainmap_dict[k]
10000 loops, best of 3: 27.1 µs per loop

So about an order of magnitude slower for lookups. I’m a fan of Chainmap, but looks less practical where there may be many lookups.


回答 17

滥用导致马修回答的单一表达解决方案:

>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> z = (lambda f=x.copy(): (f.update(y), f)[1])()
>>> z
{'a': 1, 'c': 11, 'b': 10}

您说您想要一个表达式,所以我滥用lambda了绑定一个名称,并使用元组重写了lambda的一个表达式的限制。随时畏缩。

如果您不关心复制它,当然也可以这样做:

>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> z = (x.update(y), x)[1]
>>> z
{'a': 1, 'b': 10, 'c': 11}

Abuse leading to a one-expression solution for Matthew’s answer:

>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> z = (lambda f=x.copy(): (f.update(y), f)[1])()
>>> z
{'a': 1, 'c': 11, 'b': 10}

You said you wanted one expression, so I abused lambda to bind a name, and tuples to override lambda’s one-expression limit. Feel free to cringe.

You could also do this of course if you don’t care about copying it:

>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> z = (x.update(y), x)[1]
>>> z
{'a': 1, 'b': 10, 'c': 11}

回答 18

使用itertools的简单解决方案,该命令可保留顺序(后格优先)

import itertools as it
merge = lambda *args: dict(it.chain.from_iterable(it.imap(dict.iteritems, args)))

它的用法是:

>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> merge(x, y)
{'a': 1, 'b': 10, 'c': 11}

>>> z = {'c': 3, 'd': 4}
>>> merge(x, y, z)
{'a': 1, 'b': 10, 'c': 3, 'd': 4}

Simple solution using itertools that preserves order (latter dicts have precedence)

import itertools as it
merge = lambda *args: dict(it.chain.from_iterable(it.imap(dict.iteritems, args)))

And it’s usage:

>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> merge(x, y)
{'a': 1, 'b': 10, 'c': 11}

>>> z = {'c': 3, 'd': 4}
>>> merge(x, y, z)
{'a': 1, 'b': 10, 'c': 3, 'd': 4}

回答 19

两本字典

def union2(dict1, dict2):
    return dict(list(dict1.items()) + list(dict2.items()))

n词典

def union(*dicts):
    return dict(itertools.chain.from_iterable(dct.items() for dct in dicts))

sum性能不佳。参见https://mathieularose.com/how-not-to-flatten-a-list-of-lists-in-python/

Two dictionaries

def union2(dict1, dict2):
    return dict(list(dict1.items()) + list(dict2.items()))

n dictionaries

def union(*dicts):
    return dict(itertools.chain.from_iterable(dct.items() for dct in dicts))

sum has bad performance. See https://mathieularose.com/how-not-to-flatten-a-list-of-lists-in-python/


回答 20

即使答案对于此浅表字典而言是好的,但此处定义的方法均未进行深字典合并。

示例如下:

a = { 'one': { 'depth_2': True }, 'two': True }
b = { 'one': { 'extra': False } }
print dict(a.items() + b.items())

人们会期望这样的结果:

{ 'one': { 'extra': False', 'depth_2': True }, 'two': True }

相反,我们得到以下信息:

{'two': True, 'one': {'extra': False}}

如果“ one”条目确实是合并的,则其字典中的项目应具有“ depth_2”和“ extra”作为条目。

也使用链,不起作用:

from itertools import chain
print dict(chain(a.iteritems(), b.iteritems()))

结果是:

{'two': True, 'one': {'extra': False}}

rcwesick进行的深度合并也产生相同的结果。

是的,可以合并示例字典,但是它们都不是合并的通用机制。一旦编写了可以真正合并的方法,我将在以后进行更新。

Even though the answers were good for this shallow dictionary, none of the methods defined here actually do a deep dictionary merge.

Examples follow:

a = { 'one': { 'depth_2': True }, 'two': True }
b = { 'one': { 'extra': False } }
print dict(a.items() + b.items())

One would expect a result of something like this:

{ 'one': { 'extra': False', 'depth_2': True }, 'two': True }

Instead, we get this:

{'two': True, 'one': {'extra': False}}

The ‘one’ entry should have had ‘depth_2’ and ‘extra’ as items inside its dictionary if it truly was a merge.

Using chain also, does not work:

from itertools import chain
print dict(chain(a.iteritems(), b.iteritems()))

Results in:

{'two': True, 'one': {'extra': False}}

The deep merge that rcwesick gave also creates the same result.

Yes, it will work to merge the sample dictionaries, but none of them are a generic mechanism to merge. I’ll update this later once I write a method that does a true merge.


回答 21

(仅适用于Python2.7 *;对于Python3 *有更简单的解决方案。)

如果您不反对导入标准库模块,则可以执行

from functools import reduce

def merge_dicts(*dicts):
    return reduce(lambda a, d: a.update(d) or a, dicts, {})

(由于总是返回成功,所以中的or alambda是必需的。)dict.updateNone

(For Python2.7* only; there are simpler solutions for Python3*.)

If you’re not averse to importing a standard library module, you can do

from functools import reduce

def merge_dicts(*dicts):
    return reduce(lambda a, d: a.update(d) or a, dicts, {})

(The or a bit in the lambda is necessary because dict.update always returns None on success.)


回答 22

如果您不介意变异x

x.update(y) or x

简单,可读,高效。您知道 update()总是会返回None,这是一个错误的值。因此,上述表达式x在更新后将始终等于。

标准库中的变异方法(如.update()None按约定返回,因此该模式也适用于这些方法。如果您使用的方法不遵循此约定,则or可能无法正常工作。但是,您可以使用元组显示和索引来使其成为单个表达式。无论第一个元素的计算结果如何,此方法都有效。

(x.update(y), x)[-1]

如果还没有x变量,则可以使用lambda本地变量而不使用赋值语句。这相当于lambda用作let表达式,这是功能语言中的一种常用技术,但可能不是Python语言。

(lambda x: x.update(y) or x)({'a': 1, 'b': 2})

尽管与下面使用新的walrus运算符(仅适用于Python 3.8+)没有什么不同:

(x := {'a': 1, 'b': 2}).update(y) or x

如果您确实想要复制,则PEP 448样式最简单{**x, **y}。但是,如果您的(旧)Python版本中没有该功能,则let模式也可以在这里使用。

(lambda z: z.update(y) or z)(x.copy())

(当然,这等效于(z := x.copy()).update(y) or z,但是如果您的Python版本足够新,则可以使用PEP 448样式。)

If you don’t mind mutating x,

x.update(y) or x

Simple, readable, performant. You know update() always returns None, which is a false value. So the above expression will always evaluate to x, after updating it.

Mutating methods in the standard library (like .update()) return None by convention, so this pattern will work on those too. If you’re using a method that doesn’t follow this convention, then or may not work. But, you can use a tuple display and index to make it a single expression, instead. This works regardless of what the first element evaluates to.

(x.update(y), x)[-1]

If you don’t have x in a variable yet, you can use lambda to make a local without using an assignment statement. This amounts to using lambda as a let expression, which is a common technique in functional languages, but maybe unpythonic.

(lambda x: x.update(y) or x)({'a': 1, 'b': 2})

Although it’s not that different from the following use of the new walrus operator (Python 3.8+ only):

(x := {'a': 1, 'b': 2}).update(y) or x

If you do want a copy, PEP 448 style is easiest {**x, **y}. But if that’s not available in your (older) Python version, the let pattern works here too.

(lambda z: z.update(y) or z)(x.copy())

(That is, of course, equivalent to (z := x.copy()).update(y) or z, but if your Python version is new enough for that, then the PEP 448 style will be available.)


回答 23

借鉴这里和其他地方的想法,我理解了一个函数:

def merge(*dicts, **kv): 
      return { k:v for d in list(dicts) + [kv] for k,v in d.items() }

用法(在python 3中测试):

assert (merge({1:11,'a':'aaa'},{1:99, 'b':'bbb'},foo='bar')==\
    {1: 99, 'foo': 'bar', 'b': 'bbb', 'a': 'aaa'})

assert (merge(foo='bar')=={'foo': 'bar'})

assert (merge({1:11},{1:99},foo='bar',baz='quux')==\
    {1: 99, 'foo': 'bar', 'baz':'quux'})

assert (merge({1:11},{1:99})=={1: 99})

您可以改用lambda。

Drawing on ideas here and elsewhere I’ve comprehended a function:

def merge(*dicts, **kv): 
      return { k:v for d in list(dicts) + [kv] for k,v in d.items() }

Usage (tested in python 3):

assert (merge({1:11,'a':'aaa'},{1:99, 'b':'bbb'},foo='bar')==\
    {1: 99, 'foo': 'bar', 'b': 'bbb', 'a': 'aaa'})

assert (merge(foo='bar')=={'foo': 'bar'})

assert (merge({1:11},{1:99},foo='bar',baz='quux')==\
    {1: 99, 'foo': 'bar', 'baz':'quux'})

assert (merge({1:11},{1:99})=={1: 99})

You could use a lambda instead.


回答 24

迄今为止,我列出的解决方案存在的问题是,在合并的词典中,键“ b”的值是10,但按照我的想法,应该是12。鉴于此,我提出以下内容:

import timeit

n=100000
su = """
x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
"""

def timeMerge(f,su,niter):
    print "{:4f} sec for: {:30s}".format(timeit.Timer(f,setup=su).timeit(n),f)

timeMerge("dict(x, **y)",su,n)
timeMerge("x.update(y)",su,n)
timeMerge("dict(x.items() + y.items())",su,n)
timeMerge("for k in y.keys(): x[k] = k in x and x[k]+y[k] or y[k] ",su,n)

#confirm for loop adds b entries together
x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
for k in y.keys(): x[k] = k in x and x[k]+y[k] or y[k]
print "confirm b elements are added:",x

结果:

0.049465 sec for: dict(x, **y)
0.033729 sec for: x.update(y)                   
0.150380 sec for: dict(x.items() + y.items())   
0.083120 sec for: for k in y.keys(): x[k] = k in x and x[k]+y[k] or y[k]

confirm b elements are added: {'a': 1, 'c': 11, 'b': 12}

The problem I have with solutions listed to date is that, in the merged dictionary, the value for key “b” is 10 but, to my way of thinking, it should be 12. In that light, I present the following:

import timeit

n=100000
su = """
x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
"""

def timeMerge(f,su,niter):
    print "{:4f} sec for: {:30s}".format(timeit.Timer(f,setup=su).timeit(n),f)

timeMerge("dict(x, **y)",su,n)
timeMerge("x.update(y)",su,n)
timeMerge("dict(x.items() + y.items())",su,n)
timeMerge("for k in y.keys(): x[k] = k in x and x[k]+y[k] or y[k] ",su,n)

#confirm for loop adds b entries together
x = {'a':1, 'b': 2}
y = {'b':10, 'c': 11}
for k in y.keys(): x[k] = k in x and x[k]+y[k] or y[k]
print "confirm b elements are added:",x

Results:

0.049465 sec for: dict(x, **y)
0.033729 sec for: x.update(y)                   
0.150380 sec for: dict(x.items() + y.items())   
0.083120 sec for: for k in y.keys(): x[k] = k in x and x[k]+y[k] or y[k]

confirm b elements are added: {'a': 1, 'c': 11, 'b': 12}

回答 25

真傻,.update什么也没返回。
我只是使用一个简单的辅助函数来解决问题:

def merge(dict1,*dicts):
    for dict2 in dicts:
        dict1.update(dict2)
    return dict1

例子:

merge(dict1,dict2)
merge(dict1,dict2,dict3)
merge(dict1,dict2,dict3,dict4)
merge({},dict1,dict2)  # this one returns a new copy

It’s so silly that .update returns nothing.
I just use a simple helper function to solve the problem:

def merge(dict1,*dicts):
    for dict2 in dicts:
        dict1.update(dict2)
    return dict1

Examples:

merge(dict1,dict2)
merge(dict1,dict2,dict3)
merge(dict1,dict2,dict3,dict4)
merge({},dict1,dict2)  # this one returns a new copy

回答 26

from collections import Counter
dict1 = {'a':1, 'b': 2}
dict2 = {'b':10, 'c': 11}
result = dict(Counter(dict1) + Counter(dict2))

这应该可以解决您的问题。

from collections import Counter
dict1 = {'a':1, 'b': 2}
dict2 = {'b':10, 'c': 11}
result = dict(Counter(dict1) + Counter(dict2))

This should solve your problem.


回答 27

这可以通过单个dict理解来完成:

>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> { key: y[key] if key in y else x[key]
      for key in set(x) + set(y)
    }

在我看来,“单个表达式”部分的最佳答案是因为不需要额外的功能,而且它很简短。

This can be done with a single dict comprehension:

>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> { key: y[key] if key in y else x[key]
      for key in set(x) + set(y)
    }

In my view the best answer for the ‘single expression’ part as no extra functions are needed, and it is short.


回答 28

由于PEP 572:Assignment Expressions,Python 3.8发行版(计划于2019年10月20日)将提供一个新选项。新的赋值表达式运算符使您可以分配的结果,并仍然使用它来调用,从而使组合的代码成为单个表达式,而不是两个语句,从而进行了更改::=copyupdate

newdict = dict1.copy()
newdict.update(dict2)

至:

(newdict := dict1.copy()).update(dict2)

同时在各个方面都表现相同。如果还必须返回结果dict(您要求返回的表达式dict;上面创建并分配给newdict,但没有返回,因此您不能使用它将参数直接传递给函数la myfunc((newdict := dict1.copy()).update(dict2))) ,然后将其添加or newdict到末尾(因为updatereturns None是虚假的,因此它将求值并newdict作为表达式的结果返回):

(newdict := dict1.copy()).update(dict2) or newdict

重要警告:通常,我不建议采用以下方法:

newdict = {**dict1, **dict2}

拆包方法更清晰(对于一开始就知道要进行广义拆包的人来说,应该这样),根本不需要名称(因此,构造一个立即传递给a的临时文件时,它会更加简洁。函数或包含在list/ tuple文字等中),并且几乎肯定也更快,因为(在CPython上)大致等同于:

newdict = {}
newdict.update(dict1)
newdict.update(dict2)

但使用具体的dictAPI 在C层完成,因此不涉及动态方法查找/绑定或函数调用分派开销(在此(newdict := dict1.copy()).update(dict2)情况下,行为不可避免地与原始的两层相同,在不连续的步骤中执行工作,并进行动态查找/绑定/方法的调用。

它也更可扩展,因为合并三个dicts是显而易见的:

 newdict = {**dict1, **dict2, **dict3}

使用赋值表达式不会像这样缩放的地方;您能得到的最接近的是:

 (newdict := dict1.copy()).update(dict2), newdict.update(dict3)

或没有Nones 的临时元组,但对每个None结果进行真实性测试:

 (newdict := dict1.copy()).update(dict2) or newdict.update(dict3)

其中的任一个是明显更恶心,并且包括进一步的低效(或者是临时浪费tupleNoneS表示逗号分离,或每个的无意义感实性测试updateNone用于返回or分离)。

赋值表达式方法的唯一真正优势在于:

  1. 您有需要同时处理sets和dicts的通用代码(它们都支持copyupdate,因此代码可以按您期望的那样大致工作)
  2. 您期望接收任意类似dict的对象,而不仅仅是dict自身,并且必须保留左侧的类型和语义(而不是以简单的结尾dict)。尽管myspecialdict({**speciala, **specialb})可能会起作用,但它会涉及一个额外的临时操作dict,并且如果myspecialdict具有平原dict无法保留的功能(例如,常规dicts现在基于键的首次出现保留顺序,而基于键的最后出现保留值;您可能想要一个根据最后一个保留订单键的外观,因此更新值也会将其移到末尾),那么语义将是错误的。由于赋值表达式版本使用命名方法(可能会重载以使其正常运行),因此它根本不会创建一个dict(除非dict1已经是一个dict),并保留原始类型(和原始类型的语义),同时避免任何临时性。

There will be a new option when Python 3.8 releases (scheduled for 20 October, 2019), thanks to PEP 572: Assignment Expressions. The new assignment expression operator := allows you to assign the result of the copy and still use it to call update, leaving the combined code a single expression, rather than two statements, changing:

newdict = dict1.copy()
newdict.update(dict2)

to:

(newdict := dict1.copy()).update(dict2)

while behaving identically in every way. If you must also return the resulting dict (you asked for an expression returning the dict; the above creates and assigns to newdict, but doesn’t return it, so you couldn’t use it to pass an argument to a function as is, a la myfunc((newdict := dict1.copy()).update(dict2))), then just add or newdict to the end (since update returns None, which is falsy, it will then evaluate and return newdict as the result of the expression):

(newdict := dict1.copy()).update(dict2) or newdict

Important caveat: In general, I’d discourage this approach in favor of:

newdict = {**dict1, **dict2}

The unpacking approach is clearer (to anyone who knows about generalized unpacking in the first place, which you should), doesn’t require a name for the result at all (so it’s much more concise when constructing a temporary that is immediately passed to a function or included in a list/tuple literal or the like), and is almost certainly faster as well, being (on CPython) roughly equivalent to:

newdict = {}
newdict.update(dict1)
newdict.update(dict2)

but done at the C layer, using the concrete dict API, so no dynamic method lookup/binding or function call dispatch overhead is involved (where (newdict := dict1.copy()).update(dict2) is unavoidably identical to the original two-liner in behavior, performing the work in discrete steps, with dynamic lookup/binding/invocation of methods.

It’s also more extensible, as merging three dicts is obvious:

 newdict = {**dict1, **dict2, **dict3}

where using assignment expressions won’t scale like that; the closest you could get would be:

 (newdict := dict1.copy()).update(dict2), newdict.update(dict3)

or without the temporary tuple of Nones, but with truthiness testing of each None result:

 (newdict := dict1.copy()).update(dict2) or newdict.update(dict3)

either of which is obviously much uglier, and includes further inefficiencies (either a wasted temporary tuple of Nones for comma separation, or pointless truthiness testing of each update‘s None return for or separation).

The only real advantage to the assignment expression approach occurs if:

  1. You have generic code that needs handle both sets and dicts (both of them support copy and update, so the code works roughly as you’d expect it to)
  2. You expect to receive arbitrary dict-like objects, not just dict itself, and must preserve the type and semantics of the left hand side (rather than ending up with a plain dict). While myspecialdict({**speciala, **specialb}) might work, it would involve an extra temporary dict, and if myspecialdict has features plain dict can’t preserve (e.g. regular dicts now preserve order based on the first appearance of a key, and value based on the last appearance of a key; you might want one that preserves order based on the last appearance of a key so updating a value also moves it to the end), then the semantics would be wrong. Since the assignment expression version uses the named methods (which are presumably overloaded to behave appropriately), it never creates a dict at all (unless dict1 was already a dict), preserving the original type (and original type’s semantics), all while avoiding any temporaries.

回答 29

>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> x, z = dict(x), x.update(y) or x
>>> x
{'a': 1, 'b': 2}
>>> y
{'c': 11, 'b': 10}
>>> z
{'a': 1, 'c': 11, 'b': 10}
>>> x = {'a':1, 'b': 2}
>>> y = {'b':10, 'c': 11}
>>> x, z = dict(x), x.update(y) or x
>>> x
{'a': 1, 'b': 2}
>>> y
{'c': 11, 'b': 10}
>>> z
{'a': 1, 'c': 11, 'b': 10}

如何按值对字典排序?

问题:如何按值对字典排序?

我有一个从数据库的两个字段中读取的值的字典:字符串字段和数字字段。字符串字段是唯一的,因此这是字典的键。

我可以对键进行排序,但是如何根据值进行排序?

注意:我在这里阅读了堆栈溢出问题,如何按字典值对字典列表进行排序?可能会更改我的代码以包含字典列表,但是由于我实际上并不需要字典列表,因此我想知道是否存在一种更简单的解决方案来按升序或降序进行排序。

I have a dictionary of values read from two fields in a database: a string field and a numeric field. The string field is unique, so that is the key of the dictionary.

I can sort on the keys, but how can I sort based on the values?

Note: I have read Stack Overflow question here How do I sort a list of dictionaries by a value of the dictionary? and probably could change my code to have a list of dictionaries, but since I do not really need a list of dictionaries I wanted to know if there is a simpler solution to sort either in ascending or descending order.


回答 0

Python 3.6+

x = {1: 2, 3: 4, 4: 3, 2: 1, 0: 0}
{k: v for k, v in sorted(x.items(), key=lambda item: item[1])}
{0: 0, 2: 1, 1: 2, 4: 3, 3: 4}

旧版Python

无法对字典进行排序,只能获得已排序字典的表示形式。字典本质上是无序的,但其他类型(例如列表和元组)不是。因此,您需要一个有序的数据类型来表示排序后的值,该值将是一个列表-可能是一个元组列表。

例如,

import operator
x = {1: 2, 3: 4, 4: 3, 2: 1, 0: 0}
sorted_x = sorted(x.items(), key=operator.itemgetter(1))

sorted_x将是按每个元组中第二个元素排序的元组列表。dict(sorted_x) == x

对于那些希望对键而不是值进行排序的人:

import operator
x = {1: 2, 3: 4, 4: 3, 2: 1, 0: 0}
sorted_x = sorted(x.items(), key=operator.itemgetter(0))

在Python3中,由于不允许拆包[1],我们可以使用

x = {1: 2, 3: 4, 4: 3, 2: 1, 0: 0}
sorted_x = sorted(x.items(), key=lambda kv: kv[1])

如果要将输出作为字典,则可以使用collections.OrderedDict

import collections

sorted_dict = collections.OrderedDict(sorted_x)

Python 3.6+

x = {1: 2, 3: 4, 4: 3, 2: 1, 0: 0}
{k: v for k, v in sorted(x.items(), key=lambda item: item[1])}
{0: 0, 2: 1, 1: 2, 4: 3, 3: 4}

Older Python

It is not possible to sort a dictionary, only to get a representation of a dictionary that is sorted. Dictionaries are inherently orderless, but other types, such as lists and tuples, are not. So you need an ordered data type to represent sorted values, which will be a list—probably a list of tuples.

For instance,

import operator
x = {1: 2, 3: 4, 4: 3, 2: 1, 0: 0}
sorted_x = sorted(x.items(), key=operator.itemgetter(1))

sorted_x will be a list of tuples sorted by the second element in each tuple. dict(sorted_x) == x.

And for those wishing to sort on keys instead of values:

import operator
x = {1: 2, 3: 4, 4: 3, 2: 1, 0: 0}
sorted_x = sorted(x.items(), key=operator.itemgetter(0))

In Python3 since unpacking is not allowed [1] we can use

x = {1: 2, 3: 4, 4: 3, 2: 1, 0: 0}
sorted_x = sorted(x.items(), key=lambda kv: kv[1])

If you want the output as a dict, you can use collections.OrderedDict:

import collections

sorted_dict = collections.OrderedDict(sorted_x)

回答 1

简单如: sorted(dict1, key=dict1.get)

好吧,实际上可以执行“按字典值排序”。最近,我不得不在Code Golf(堆栈溢出问题Code golf:单词频率图表)中进行此操作。简而言之,问题是这样的:给定文本,计算遇到每个单词的频率,并显示按频率递减排序的最重要单词列表。

如果您以单词为键构建字典,每个单词的出现次数为值,则简化为:

from collections import defaultdict
d = defaultdict(int)
for w in text.split():
    d[w] += 1

那么您可以获取单词列表,sorted(d, key=d.get)并按使用频率排序-使用单词出现的次数作为排序键,对字典键进行排序迭代。

for w in sorted(d, key=d.get, reverse=True):
    print(w, d[w])

我正在写这个详细的说明,以说明人们通常所说的“我可以很容易地按键对字典进行排序,但是如何按值进行排序”的意思-我认为原始帖子试图解决这样的问题。解决方案是根据值对键列表进行排序,如上所示。

As simple as: sorted(dict1, key=dict1.get)

Well, it is actually possible to do a “sort by dictionary values”. Recently I had to do that in a Code Golf (Stack Overflow question Code golf: Word frequency chart). Abridged, the problem was of the kind: given a text, count how often each word is encountered and display a list of the top words, sorted by decreasing frequency.

If you construct a dictionary with the words as keys and the number of occurrences of each word as value, simplified here as:

from collections import defaultdict
d = defaultdict(int)
for w in text.split():
    d[w] += 1

then you can get a list of the words, ordered by frequency of use with sorted(d, key=d.get) – the sort iterates over the dictionary keys, using the number of word occurrences as a sort key .

for w in sorted(d, key=d.get, reverse=True):
    print(w, d[w])

I am writing this detailed explanation to illustrate what people often mean by “I can easily sort a dictionary by key, but how do I sort by value” – and I think the original post was trying to address such an issue. And the solution is to do sort of list of the keys, based on the values, as shown above.


回答 2

您可以使用:

sorted(d.items(), key=lambda x: x[1])

这将按照字典中每个条目的值(从最小到最大)对字典进行排序。

要对其进行降序排序,只需添加reverse=True

sorted(d.items(), key=lambda x: x[1], reverse=True)

输入:

d = {'one':1,'three':3,'five':5,'two':2,'four':4}
a = sorted(d.items(), key=lambda x: x[1])    
print(a)

输出:

[('one', 1), ('two', 2), ('three', 3), ('four', 4), ('five', 5)]

You could use:

sorted(d.items(), key=lambda x: x[1])

This will sort the dictionary by the values of each entry within the dictionary from smallest to largest.

To sort it in descending order just add reverse=True:

sorted(d.items(), key=lambda x: x[1], reverse=True)

Input:

d = {'one':1,'three':3,'five':5,'two':2,'four':4}
a = sorted(d.items(), key=lambda x: x[1])    
print(a)

Output:

[('one', 1), ('two', 2), ('three', 3), ('four', 4), ('five', 5)]

回答 3

字典无法排序,但您可以从中建立排序列表。

字典值的排序列表:

sorted(d.values())

(键,值)对的列表,按值排序:

from operator import itemgetter
sorted(d.items(), key=itemgetter(1))

Dicts can’t be sorted, but you can build a sorted list from them.

A sorted list of dict values:

sorted(d.values())

A list of (key, value) pairs, sorted by value:

from operator import itemgetter
sorted(d.items(), key=itemgetter(1))

回答 4

在最近的Python 2.7中,我们有了新的OrderedDict类型,该类型可以记住添加项目的顺序。

>>> d = {"third": 3, "first": 1, "fourth": 4, "second": 2}

>>> for k, v in d.items():
...     print "%s: %s" % (k, v)
...
second: 2
fourth: 4
third: 3
first: 1

>>> d
{'second': 2, 'fourth': 4, 'third': 3, 'first': 1}

要从原始字典中重新排序,请按以下值排序:

>>> from collections import OrderedDict
>>> d_sorted_by_value = OrderedDict(sorted(d.items(), key=lambda x: x[1]))

OrderedDict的行为类似于普通字典:

>>> for k, v in d_sorted_by_value.items():
...     print "%s: %s" % (k, v)
...
first: 1
second: 2
third: 3
fourth: 4

>>> d_sorted_by_value
OrderedDict([('first': 1), ('second': 2), ('third': 3), ('fourth': 4)])

In recent Python 2.7, we have the new OrderedDict type, which remembers the order in which the items were added.

>>> d = {"third": 3, "first": 1, "fourth": 4, "second": 2}

>>> for k, v in d.items():
...     print "%s: %s" % (k, v)
...
second: 2
fourth: 4
third: 3
first: 1

>>> d
{'second': 2, 'fourth': 4, 'third': 3, 'first': 1}

To make a new ordered dictionary from the original, sorting by the values:

>>> from collections import OrderedDict
>>> d_sorted_by_value = OrderedDict(sorted(d.items(), key=lambda x: x[1]))

The OrderedDict behaves like a normal dict:

>>> for k, v in d_sorted_by_value.items():
...     print "%s: %s" % (k, v)
...
first: 1
second: 2
third: 3
fourth: 4

>>> d_sorted_by_value
OrderedDict([('first': 1), ('second': 2), ('third': 3), ('fourth': 4)])

回答 5

更新:2015年12月5日使用Python 3.5

尽管我发现接受的答案很有用,但令我感到惊讶的是,它没有被更新为从标准库集合模块中引用OrderedDict作为可行的现代替代方案,旨在解决这类问题。

from operator import itemgetter
from collections import OrderedDict

x = {1: 2, 3: 4, 4: 3, 2: 1, 0: 0}
sorted_x = OrderedDict(sorted(x.items(), key=itemgetter(1)))
# OrderedDict([(0, 0), (2, 1), (1, 2), (4, 3), (3, 4)])

官方的OrderedDict文档也提供了一个非常相似的示例,但是对排序函数使用了lambda:

# regular unsorted dictionary
d = {'banana': 3, 'apple':4, 'pear': 1, 'orange': 2}

# dictionary sorted by value
OrderedDict(sorted(d.items(), key=lambda t: t[1]))
# OrderedDict([('pear', 1), ('orange', 2), ('banana', 3), ('apple', 4)])

UPDATE: 5 DECEMBER 2015 using Python 3.5

Whilst I found the accepted answer useful, I was also surprised that it hasn’t been updated to reference OrderedDict from the standard library collections module as a viable, modern alternative – designed to solve exactly this type of problem.

from operator import itemgetter
from collections import OrderedDict

x = {1: 2, 3: 4, 4: 3, 2: 1, 0: 0}
sorted_x = OrderedDict(sorted(x.items(), key=itemgetter(1)))
# OrderedDict([(0, 0), (2, 1), (1, 2), (4, 3), (3, 4)])

The official OrderedDict documentation offers a very similar example too, but using a lambda for the sort function:

# regular unsorted dictionary
d = {'banana': 3, 'apple':4, 'pear': 1, 'orange': 2}

# dictionary sorted by value
OrderedDict(sorted(d.items(), key=lambda t: t[1]))
# OrderedDict([('pear', 1), ('orange', 2), ('banana', 3), ('apple', 4)])

回答 6

Hank Gay的答案几乎相同:

sorted([(value,key) for (key,value) in mydict.items()])

或根据John Fouhy的建议进行了稍微优化:

sorted((value,key) for (key,value) in mydict.items())

Pretty much the same as Hank Gay’s answer:

sorted([(value,key) for (key,value) in mydict.items()])

Or optimized slightly as suggested by John Fouhy:

sorted((value,key) for (key,value) in mydict.items())

回答 7

使用namedtuple通常很方便。例如,您有一个“名称”作为键,而“分数”作为值的字典,并且您想对“分数”进行排序:

import collections
Player = collections.namedtuple('Player', 'score name')
d = {'John':5, 'Alex':10, 'Richard': 7}

首先以最低分数排序:

worst = sorted(Player(v,k) for (k,v) in d.items())

首先以最高分排序:

best = sorted([Player(v,k) for (k,v) in d.items()], reverse=True)

现在您可以得到Python的第二好玩家(index = 1)的名称和得分,如下所示:

player = best[1]
player.name
    'Richard'
player.score
    7

It can often be very handy to use namedtuple. For example, you have a dictionary of ‘name’ as keys and ‘score’ as values and you want to sort on ‘score’:

import collections
Player = collections.namedtuple('Player', 'score name')
d = {'John':5, 'Alex':10, 'Richard': 7}

sorting with lowest score first:

worst = sorted(Player(v,k) for (k,v) in d.items())

sorting with highest score first:

best = sorted([Player(v,k) for (k,v) in d.items()], reverse=True)

Now you can get the name and score of, let’s say the second-best player (index=1) very Pythonically like this:

player = best[1]
player.name
    'Richard'
player.score
    7

回答 8

Python 3.6开始,将对内置字典进行排序

好消息,因此OP从数据库中检索到的映射对的原始用例(以唯一的字符串ID作为键,将数值作为值)转换为内置Python v3.6 + dict,现在应该遵守插入顺序。

如果说从数据库查询中得到的两个列表表达式如下:

SELECT a_key, a_value FROM a_table ORDER BY a_value;

将存储在两个Python元组k_seq和v_seq中(按数字索引对齐,并且具有相同的长度),然后:

k_seq = ('foo', 'bar', 'baz')
v_seq = (0, 1, 42)
ordered_map = dict(zip(k_seq, v_seq))

允许以后输出为:

for k, v in ordered_map.items():
    print(k, v)

在这种情况下产生(对于新的Python 3.6+内置字典!):

foo 0
bar 1
baz 42

以v的每个值相同的顺序排列。

当前在我的机器上的Python 3.5安装位置生成:

bar 1
foo 0
baz 42

细节:

正如Raymond Hettinger在2012年提出的(请参见python-dev上的邮件,主题为“更紧凑的字典,迭代速度更快”),现在(2016年),Victor Stinner在给主题为“ Python 3.6 dict的 python-dev的邮件”中宣布紧凑并获得私有版本;由于在Python 3.6中已解决/实现了问题27350 “紧凑且有序的字典”,因此关键字变得有序”,我们现在可以使用内置的字典来维护插入顺序!!

希望这将导致第一步的薄层OrderedDict实现。就像@ JimFasarakis-Hilliard指出的那样,将来还会看到一些OrderedDict类型的用例。我认为整个Python社区都会仔细检查,是否经得起时间的考验以及下一步将是什么。

是时候重新考虑一下我们的编码习惯,不要错过以下稳定排序所带来的可能性:

  • 关键字参数和
  • (中级)字典存储

第一个是因为它在某些情况下简化了函数和方法的实现中的分派。

第二个参数鼓励使用dicts作为处理管道中的中间存储。

Raymond Hettinger 从旧金山Python Meetup Group的演讲2016-DEC-08中提供了解释“ Python 3.6词典背后的技术文档。

也许相当一部分Stack Overflow高修饰度的问答页面会收到此信息的变体,并且许多高质量的答案也需要按版本进行更新。

警告购买者(另请参阅下面的2017年12月15日更新):

正如@ajcr正确指出的那样:“此新实现的顺序保留方面被认为是实现细节,因此不应依赖。” (摘自whatsnew36)并不是很挑剔,引文有点悲观了;-)。它继续显示为“(将来可能会改变,但是希望在更改语言规范以强制所有当前和将来的Python实现保留顺序语义之前,先在几个版本中使用该语言的新dict实现;有助于保持与仍旧有效的随机迭代顺序的旧版语言(例如Python 3.5)的向后兼容性。”

因此,就像在某些人类语言(例如德语)中一样,用法决定了语言的使用方式,现在遗嘱已在whatsnew36中声明。

更新2017-12-15:

发给python-dev列表邮件中,Guido van Rossum声明:

做到这一点。裁定“裁定保留插入顺序”。谢谢!

因此,dict插入顺序的3.6 CPython版本的副作用现在已成为语言规范的一部分(并且不再仅仅是实现细节)。collections.OrderedDict正如雷蒙德·海廷格(Raymond Hettinger)在讨论中所提醒的那样,该邮件线程还浮出了一些与众不同的设计目标。

As of Python 3.6 the built-in dict will be ordered

Good news, so the OP’s original use case of mapping pairs retrieved from a database with unique string ids as keys and numeric values as values into a built-in Python v3.6+ dict, should now respect the insert order.

If say the resulting two column table expressions from a database query like:

SELECT a_key, a_value FROM a_table ORDER BY a_value;

would be stored in two Python tuples, k_seq and v_seq (aligned by numerical index and with the same length of course), then:

k_seq = ('foo', 'bar', 'baz')
v_seq = (0, 1, 42)
ordered_map = dict(zip(k_seq, v_seq))

Allow to output later as:

for k, v in ordered_map.items():
    print(k, v)

yielding in this case (for the new Python 3.6+ built-in dict!):

foo 0
bar 1
baz 42

in the same ordering per value of v.

Where in the Python 3.5 install on my machine it currently yields:

bar 1
foo 0
baz 42

Details:

As proposed in 2012 by Raymond Hettinger (cf. mail on python-dev with subject “More compact dictionaries with faster iteration”) and now (in 2016) announced in a mail by Victor Stinner to python-dev with subject “Python 3.6 dict becomes compact and gets a private version; and keywords become ordered” due to the fix/implementation of issue 27350 “Compact and ordered dict” in Python 3.6 we will now be able, to use a built-in dict to maintain insert order!!

Hopefully this will lead to a thin layer OrderedDict implementation as a first step. As @JimFasarakis-Hilliard indicated, some see use cases for the OrderedDict type also in the future. I think the Python community at large will carefully inspect, if this will stand the test of time, and what the next steps will be.

Time to rethink our coding habits to not miss the possibilities opened by stable ordering of:

  • Keyword arguments and
  • (intermediate) dict storage

The first because it eases dispatch in the implementation of functions and methods in some cases.

The second as it encourages to more easily use dicts as intermediate storage in processing pipelines.

Raymond Hettinger kindly provided documentation explaining “The Tech Behind Python 3.6 Dictionaries” – from his San Francisco Python Meetup Group presentation 2016-DEC-08.

And maybe quite some Stack Overflow high decorated question and answer pages will receive variants of this information and many high quality answers will require a per version update too.

Caveat Emptor (but also see below update 2017-12-15):

As @ajcr rightfully notes: “The order-preserving aspect of this new implementation is considered an implementation detail and should not be relied upon.” (from the whatsnew36) not nit picking, but the citation was cut a bit pessimistic ;-). It continues as ” (this may change in the future, but it is desired to have this new dict implementation in the language for a few releases before changing the language spec to mandate order-preserving semantics for all current and future Python implementations; this also helps preserve backwards-compatibility with older versions of the language where random iteration order is still in effect, e.g. Python 3.5).”

So as in some human languages (e.g. German), usage shapes the language, and the will now has been declared … in whatsnew36.

Update 2017-12-15:

In a mail to the python-dev list, Guido van Rossum declared:

Make it so. “Dict keeps insertion order” is the ruling. Thanks!

So, the version 3.6 CPython side-effect of dict insertion ordering is now becoming part of the language spec (and not anymore only an implementation detail). That mail thread also surfaced some distinguishing design goals for collections.OrderedDict as reminded by Raymond Hettinger during discussion.


回答 9

我有同样的问题,我这样解决了:

WantedOutput = sorted(MyDict, key=lambda x : MyDict[x]) 

(回答“无法对字典进行排序的人没有读过这个问题!实际上,“我可以对键进行排序,但是如何根据值进行排序?”显然意味着他想要一个列表)键根据其值的值排序。)

请注意,顺序定义不正确(具有相同值的键在输出列表中将以任意顺序排列)。

I had the same problem, and I solved it like this:

WantedOutput = sorted(MyDict, key=lambda x : MyDict[x]) 

(People who answer “It is not possible to sort a dict” did not read the question! In fact, “I can sort on the keys, but how can I sort based on the values?” clearly means that he wants a list of the keys sorted according to the value of their values.)

Please notice that the order is not well defined (keys with the same value will be in an arbitrary order in the output list).


回答 10

如果值是数字,则也可以Countercollections中使用。

from collections import Counter

x = {'hello': 1, 'python': 5, 'world': 3}
c = Counter(x)
print(c.most_common())

>> [('python', 5), ('world', 3), ('hello', 1)]    

If values are numeric you may also use Counter from collections.

from collections import Counter

x = {'hello': 1, 'python': 5, 'world': 3}
c = Counter(x)
print(c.most_common())

>> [('python', 5), ('world', 3), ('hello', 1)]    

回答 11

在Python 2.7中,只需执行以下操作:

from collections import OrderedDict
# regular unsorted dictionary
d = {'banana': 3, 'apple':4, 'pear': 1, 'orange': 2}

# dictionary sorted by key
OrderedDict(sorted(d.items(), key=lambda t: t[0]))
OrderedDict([('apple', 4), ('banana', 3), ('orange', 2), ('pear', 1)])

# dictionary sorted by value
OrderedDict(sorted(d.items(), key=lambda t: t[1]))
OrderedDict([('pear', 1), ('orange', 2), ('banana', 3), ('apple', 4)])

复制粘贴自:http : //docs.python.org/dev/library/collections.html#ordereddict-examples-and-recipes

请享用 ;-)

In Python 2.7, simply do:

from collections import OrderedDict
# regular unsorted dictionary
d = {'banana': 3, 'apple':4, 'pear': 1, 'orange': 2}

# dictionary sorted by key
OrderedDict(sorted(d.items(), key=lambda t: t[0]))
OrderedDict([('apple', 4), ('banana', 3), ('orange', 2), ('pear', 1)])

# dictionary sorted by value
OrderedDict(sorted(d.items(), key=lambda t: t[1]))
OrderedDict([('pear', 1), ('orange', 2), ('banana', 3), ('apple', 4)])

copy-paste from : http://docs.python.org/dev/library/collections.html#ordereddict-examples-and-recipes

Enjoy ;-)


回答 12

这是代码:

import operator
origin_list = [
    {"name": "foo", "rank": 0, "rofl": 20000},
    {"name": "Silly", "rank": 15, "rofl": 1000},
    {"name": "Baa", "rank": 300, "rofl": 20},
    {"name": "Zoo", "rank": 10, "rofl": 200},
    {"name": "Penguin", "rank": -1, "rofl": 10000}
]
print ">> Original >>"
for foo in origin_list:
    print foo

print "\n>> Rofl sort >>"
for foo in sorted(origin_list, key=operator.itemgetter("rofl")):
    print foo

print "\n>> Rank sort >>"
for foo in sorted(origin_list, key=operator.itemgetter("rank")):
    print foo

结果如下:

原版的

{'name': 'foo', 'rank': 0, 'rofl': 20000}
{'name': 'Silly', 'rank': 15, 'rofl': 1000}
{'name': 'Baa', 'rank': 300, 'rofl': 20}
{'name': 'Zoo', 'rank': 10, 'rofl': 200}
{'name': 'Penguin', 'rank': -1, 'rofl': 10000}

罗夫

{'name': 'Baa', 'rank': 300, 'rofl': 20}
{'name': 'Zoo', 'rank': 10, 'rofl': 200}
{'name': 'Silly', 'rank': 15, 'rofl': 1000}
{'name': 'Penguin', 'rank': -1, 'rofl': 10000}
{'name': 'foo', 'rank': 0, 'rofl': 20000}

{'name': 'Penguin', 'rank': -1, 'rofl': 10000}
{'name': 'foo', 'rank': 0, 'rofl': 20000}
{'name': 'Zoo', 'rank': 10, 'rofl': 200}
{'name': 'Silly', 'rank': 15, 'rofl': 1000}
{'name': 'Baa', 'rank': 300, 'rofl': 20}

This is the code:

import operator
origin_list = [
    {"name": "foo", "rank": 0, "rofl": 20000},
    {"name": "Silly", "rank": 15, "rofl": 1000},
    {"name": "Baa", "rank": 300, "rofl": 20},
    {"name": "Zoo", "rank": 10, "rofl": 200},
    {"name": "Penguin", "rank": -1, "rofl": 10000}
]
print ">> Original >>"
for foo in origin_list:
    print foo

print "\n>> Rofl sort >>"
for foo in sorted(origin_list, key=operator.itemgetter("rofl")):
    print foo

print "\n>> Rank sort >>"
for foo in sorted(origin_list, key=operator.itemgetter("rank")):
    print foo

Here are the results:

Original

{'name': 'foo', 'rank': 0, 'rofl': 20000}
{'name': 'Silly', 'rank': 15, 'rofl': 1000}
{'name': 'Baa', 'rank': 300, 'rofl': 20}
{'name': 'Zoo', 'rank': 10, 'rofl': 200}
{'name': 'Penguin', 'rank': -1, 'rofl': 10000}

Rofl

{'name': 'Baa', 'rank': 300, 'rofl': 20}
{'name': 'Zoo', 'rank': 10, 'rofl': 200}
{'name': 'Silly', 'rank': 15, 'rofl': 1000}
{'name': 'Penguin', 'rank': -1, 'rofl': 10000}
{'name': 'foo', 'rank': 0, 'rofl': 20000}

Rank

{'name': 'Penguin', 'rank': -1, 'rofl': 10000}
{'name': 'foo', 'rank': 0, 'rofl': 20000}
{'name': 'Zoo', 'rank': 10, 'rofl': 200}
{'name': 'Silly', 'rank': 15, 'rofl': 1000}
{'name': 'Baa', 'rank': 300, 'rofl': 20}

回答 13

请尝试以下方法。让我们用以下数据定义一个名为mydict的字典:

mydict = {'carl':40,
          'alan':2,
          'bob':1,
          'danny':3}

如果要按键对字典排序,可以执行以下操作:

for key in sorted(mydict.iterkeys()):
    print "%s: %s" % (key, mydict[key])

这应该返回以下输出:

alan: 2
bob: 1
carl: 40
danny: 3

另一方面,如果要按值对字典排序(如问题中所述),则可以执行以下操作:

for key, value in sorted(mydict.iteritems(), key=lambda (k,v): (v,k)):
    print "%s: %s" % (key, value)

该命令的结果(按值对字典进行排序)应返回以下内容:

bob: 1
alan: 2
danny: 3
carl: 40

Try the following approach. Let us define a dictionary called mydict with the following data:

mydict = {'carl':40,
          'alan':2,
          'bob':1,
          'danny':3}

If one wanted to sort the dictionary by keys, one could do something like:

for key in sorted(mydict.iterkeys()):
    print "%s: %s" % (key, mydict[key])

This should return the following output:

alan: 2
bob: 1
carl: 40
danny: 3

On the other hand, if one wanted to sort a dictionary by value (as is asked in the question), one could do the following:

for key, value in sorted(mydict.iteritems(), key=lambda (k,v): (v,k)):
    print "%s: %s" % (key, value)

The result of this command (sorting the dictionary by value) should return the following:

bob: 1
alan: 2
danny: 3
carl: 40

回答 14

从Python 3.6开始,dict对象现在按插入顺序排序。它正式在Python 3.7的规范中。

>>> words = {"python": 2, "blah": 4, "alice": 3}
>>> dict(sorted(words.items(), key=lambda x: x[1]))
{'python': 2, 'alice': 3, 'blah': 4}

在此之前,您必须使用OrderedDict

Python 3.7文档说:

在版本3.7中更改:保证字典顺序为插入顺序。此行为是3.6版CPython的实现细节。

Starting from Python 3.6, dict objects are now ordered by insertion order. It’s officially in the specs of Python 3.7.

>>> words = {"python": 2, "blah": 4, "alice": 3}
>>> dict(sorted(words.items(), key=lambda x: x[1]))
{'python': 2, 'alice': 3, 'blah': 4}

Before that, you had to use OrderedDict.

Python 3.7 documentation says:

Changed in version 3.7: Dictionary order is guaranteed to be insertion order. This behavior was implementation detail of CPython from 3.6.


回答 15

您可以创建一个“倒排索引”

from collections import defaultdict
inverse= defaultdict( list )
for k, v in originalDict.items():
    inverse[v].append( k )

现在您的逆数具有值;每个值都有一个适用键的列表。

for k in sorted(inverse):
    print k, inverse[k]

You can create an “inverted index”, also

from collections import defaultdict
inverse= defaultdict( list )
for k, v in originalDict.items():
    inverse[v].append( k )

Now your inverse has the values; each value has a list of applicable keys.

for k in sorted(inverse):
    print k, inverse[k]

回答 16

您可以使用collections.Counter。请注意,这对于数字和非数字值均适用。

>>> x = {1: 2, 3: 4, 4:3, 2:1, 0:0}
>>> from collections import Counter
>>> #To sort in reverse order
>>> Counter(x).most_common()
[(3, 4), (4, 3), (1, 2), (2, 1), (0, 0)]
>>> #To sort in ascending order
>>> Counter(x).most_common()[::-1]
[(0, 0), (2, 1), (1, 2), (4, 3), (3, 4)]
>>> #To get a dictionary sorted by values
>>> from collections import OrderedDict
>>> OrderedDict(Counter(x).most_common()[::-1])
OrderedDict([(0, 0), (2, 1), (1, 2), (4, 3), (3, 4)])

You can use the collections.Counter. Note, this will work for both numeric and non-numeric values.

>>> x = {1: 2, 3: 4, 4:3, 2:1, 0:0}
>>> from collections import Counter
>>> #To sort in reverse order
>>> Counter(x).most_common()
[(3, 4), (4, 3), (1, 2), (2, 1), (0, 0)]
>>> #To sort in ascending order
>>> Counter(x).most_common()[::-1]
[(0, 0), (2, 1), (1, 2), (4, 3), (3, 4)]
>>> #To get a dictionary sorted by values
>>> from collections import OrderedDict
>>> OrderedDict(Counter(x).most_common()[::-1])
OrderedDict([(0, 0), (2, 1), (1, 2), (4, 3), (3, 4)])

回答 17

您可以使用skip dict,这是一个按值永久排序的字典。

>>> data = {1: 2, 3: 4, 4: 3, 2: 1, 0: 0}
>>> SkipDict(data)
{0: 0.0, 2: 1.0, 1: 2.0, 4: 3.0, 3: 4.0}

如果使用keys()values()或者items()那么你会在排序顺序通过值迭代。

它是使用跳过列表数据结构实现的。

You can use a skip dict which is a dictionary that’s permanently sorted by value.

>>> data = {1: 2, 3: 4, 4: 3, 2: 1, 0: 0}
>>> SkipDict(data)
{0: 0.0, 2: 1.0, 1: 2.0, 4: 3.0, 3: 4.0}

If you use keys(), values() or items() then you’ll iterate in sorted order by value.

It’s implemented using the skip list datastructure.


回答 18

您还可以使用可以传递给键的自定义函数。

def dict_val(x):
    return x[1]
x = {1: 2, 3: 4, 4: 3, 2: 1, 0: 0}
sorted_x = sorted(x.items(), key=dict_val)

You can also use custom function that can be passed to key.

def dict_val(x):
    return x[1]
x = {1: 2, 3: 4, 4: 3, 2: 1, 0: 0}
sorted_x = sorted(x.items(), key=dict_val)

回答 19

from django.utils.datastructures import SortedDict

def sortedDictByKey(self,data):
    """Sorted dictionary order by key"""
    sortedDict = SortedDict()
    if data:
        if isinstance(data, dict):
            sortedKey = sorted(data.keys())
            for k in sortedKey:
                sortedDict[k] = data[k]
    return sortedDict
from django.utils.datastructures import SortedDict

def sortedDictByKey(self,data):
    """Sorted dictionary order by key"""
    sortedDict = SortedDict()
    if data:
        if isinstance(data, dict):
            sortedKey = sorted(data.keys())
            for k in sortedKey:
                sortedDict[k] = data[k]
    return sortedDict

回答 20

正如Dilettant指出的那样,Python 3.6现在将保持顺序!我以为我会分享我编写的一个函数,该函数可以简化可迭代对象(元组,列表,字典)的排序。在后一种情况下,您可以对键或值进行排序,并且可以考虑数字比较。仅适用于> = 3.6!

当您尝试在包含字符串和整数的可迭代对象上使用sorted时,sorted()将失败。当然,您可以使用str()强制进行字符串比较。但是,在某些情况下,您想进行小于的实际数字比较(在字符串比较中不是这种情况)。因此,我提出了以下建议。当您需要显式数值比较时,可以使用该标志,该标志将尝试通过将所有值都转换为浮点数来进行显式数值排序。如果成功,它将进行数字排序,否则将使用字符串比较。1220num_as_num

欢迎提出改进或推送要求的评论。

def sort_iterable(iterable, sort_on=None, reverse=False, num_as_num=False):
    def _sort(i):
      # sort by 0 = keys, 1 values, None for lists and tuples
      try:
        if num_as_num:
          if i is None:
            _sorted = sorted(iterable, key=lambda v: float(v), reverse=reverse)
          else:
            _sorted = dict(sorted(iterable.items(), key=lambda v: float(v[i]), reverse=reverse))
        else:
          raise TypeError
      except (TypeError, ValueError):
        if i is None:
          _sorted = sorted(iterable, key=lambda v: str(v), reverse=reverse)
        else:
          _sorted = dict(sorted(iterable.items(), key=lambda v: str(v[i]), reverse=reverse))

      return _sorted

    if isinstance(iterable, list):
      sorted_list = _sort(None)
      return sorted_list
    elif isinstance(iterable, tuple):
      sorted_list = tuple(_sort(None))
      return sorted_list
    elif isinstance(iterable, dict):
      if sort_on == 'keys':
        sorted_dict = _sort(0)
        return sorted_dict
      elif sort_on == 'values':
        sorted_dict = _sort(1)
        return sorted_dict
      elif sort_on is not None:
        raise ValueError(f"Unexpected value {sort_on} for sort_on. When sorting a dict, use key or values")
    else:
      raise TypeError(f"Unexpected type {type(iterable)} for iterable. Expected a list, tuple, or dict")

As pointed out by Dilettant, Python 3.6 will now keep the order! I thought I’d share a function I wrote that eases the sorting of an iterable (tuple, list, dict). In the latter case, you can sort either on keys or values, and it can take numeric comparison into account. Only for >= 3.6!

When you try using sorted on an iterable that holds e.g. strings as well as ints, sorted() will fail. Of course you can force string comparison with str(). However, in some cases you want to do actual numeric comparison where 12 is smaller than 20 (which is not the case in string comparison). So I came up with the following. When you want explicit numeric comparison you can use the flag num_as_num which will try to do explicit numeric sorting by trying to convert all values to floats. If that succeeds, it will do numeric sorting, otherwise it’ll resort to string comparison.

Comments for improvement or push requests welcome.

def sort_iterable(iterable, sort_on=None, reverse=False, num_as_num=False):
    def _sort(i):
      # sort by 0 = keys, 1 values, None for lists and tuples
      try:
        if num_as_num:
          if i is None:
            _sorted = sorted(iterable, key=lambda v: float(v), reverse=reverse)
          else:
            _sorted = dict(sorted(iterable.items(), key=lambda v: float(v[i]), reverse=reverse))
        else:
          raise TypeError
      except (TypeError, ValueError):
        if i is None:
          _sorted = sorted(iterable, key=lambda v: str(v), reverse=reverse)
        else:
          _sorted = dict(sorted(iterable.items(), key=lambda v: str(v[i]), reverse=reverse))

      return _sorted

    if isinstance(iterable, list):
      sorted_list = _sort(None)
      return sorted_list
    elif isinstance(iterable, tuple):
      sorted_list = tuple(_sort(None))
      return sorted_list
    elif isinstance(iterable, dict):
      if sort_on == 'keys':
        sorted_dict = _sort(0)
        return sorted_dict
      elif sort_on == 'values':
        sorted_dict = _sort(1)
        return sorted_dict
      elif sort_on is not None:
        raise ValueError(f"Unexpected value {sort_on} for sort_on. When sorting a dict, use key or values")
    else:
      raise TypeError(f"Unexpected type {type(iterable)} for iterable. Expected a list, tuple, or dict")

回答 21

这是在d.values()d.keys()上使用zip的解决方案。该链接(在Dictionary视图对象上)下面的几行是:

这允许使用zip()创建(值,键)对:pair = zip(d.values(),d.keys())。

因此,我们可以执行以下操作:

d = {'key1': 874.7, 'key2': 5, 'key3': 8.1}

d_sorted = sorted(zip(d.values(), d.keys()))

print d_sorted 
# prints: [(5, 'key2'), (8.1, 'key3'), (874.7, 'key1')]

Here is a solution using zip on d.values() and d.keys(). A few lines down this link (on Dictionary view objects) is:

This allows the creation of (value, key) pairs using zip(): pairs = zip(d.values(), d.keys()).

So we can do the following:

d = {'key1': 874.7, 'key2': 5, 'key3': 8.1}

d_sorted = sorted(zip(d.values(), d.keys()))

print d_sorted 
# prints: [(5, 'key2'), (8.1, 'key3'), (874.7, 'key1')]

回答 22

当然,请记住,您需要使用它,OrderedDict因为常规Python字典不会保留原始顺序。

from collections import OrderedDict
a = OrderedDict(sorted(originalDict.items(), key=lambda x: x[1]))

如果您没有Python 2.7或更高版本,则最好的办法是迭代生成器函数中的值。(有一个OrderedDict2.4和2.6 在这里,但

a)我不知道它的效果如何

b)当然,您必须下载并安装它。如果您没有管理权限,那么恐怕该选项不可用了。)


def gen(originalDict):
    for x, y in sorted(zip(originalDict.keys(), originalDict.values()), key=lambda z: z[1]):
        yield (x, y)
    #Yields as a tuple with (key, value). You can iterate with conditional clauses to get what you want. 

for bleh, meh in gen(myDict):
    if bleh == "foo":
        print(myDict[bleh])

您还可以打印出每个值

for bleh, meh in gen(myDict):
    print(bleh, meh)

如果未使用Python 3.0或更高版本,请记住在打印后删除括号

Of course, remember, you need to use OrderedDict because regular Python dictionaries don’t keep the original order.

from collections import OrderedDict
a = OrderedDict(sorted(originalDict.items(), key=lambda x: x[1]))

If you do not have Python 2.7 or higher, the best you can do is iterate over the values in a generator function. (There is an OrderedDict for 2.4 and 2.6 here, but

a) I don’t know about how well it works

and

b) You have to download and install it of course. If you do not have administrative access, then I’m afraid the option’s out.)


def gen(originalDict):
    for x, y in sorted(zip(originalDict.keys(), originalDict.values()), key=lambda z: z[1]):
        yield (x, y)
    #Yields as a tuple with (key, value). You can iterate with conditional clauses to get what you want. 

for bleh, meh in gen(myDict):
    if bleh == "foo":
        print(myDict[bleh])

You can also print out every value

for bleh, meh in gen(myDict):
    print(bleh, meh)

Please remember to remove the parentheses after print if not using Python 3.0 or above


回答 23

使用ValueSortedDicthttp://stardict.sourceforge.net/Dictionaries.php下载

from dicts.sorteddict import ValueSortedDict
d = {1: 2, 3: 4, 4:3, 2:1, 0:0}
sorted_dict = ValueSortedDict(d)
print sorted_dict.items() 

[(0, 0), (2, 1), (1, 2), (4, 3), (3, 4)]

Use ValueSortedDict from dicts:

from dicts.sorteddict import ValueSortedDict
d = {1: 2, 3: 4, 4:3, 2:1, 0:0}
sorted_dict = ValueSortedDict(d)
print sorted_dict.items() 

[(0, 0), (2, 1), (1, 2), (4, 3), (3, 4)]

回答 24

这适用于3.1.x:

import operator
slovar_sorted=sorted(slovar.items(), key=operator.itemgetter(1), reverse=True)
print(slovar_sorted)

This works in 3.1.x:

import operator
slovar_sorted=sorted(slovar.items(), key=operator.itemgetter(1), reverse=True)
print(slovar_sorted)

回答 25

刚刚从Python for Everyone中学习了相关技能。

您可以使用一个临时列表来帮助您对字典进行排序:

#Assume dictionary to be:
d = {'apple': 500.1, 'banana': 1500.2, 'orange': 1.0, 'pineapple': 789.0}

# create a temporary list
tmp = []

# iterate through the dictionary and append each tuple into the temporary list 
for key, value in d.items():
    tmptuple = (value, key)
    tmp.append(tmptuple)

# sort the list in ascending order
tmp = sorted(tmp)

print (tmp)

如果要按降序对列表进行排序,只需将原始排序行更改为:

tmp = sorted(tmp, reverse=True)

使用列表推导,一个衬里将是:

#Assuming the dictionary looks like
d = {'apple': 500.1, 'banana': 1500.2, 'orange': 1.0, 'pineapple': 789.0}
#One liner for sorting in ascending order
print (sorted([(v, k) for k, v in d.items()]))
#One liner for sorting in descending order
print (sorted([(v, k) for k, v in d.items()], reverse=True))

样本输出:

#Asending order
[(1.0, 'orange'), (500.1, 'apple'), (789.0, 'pineapple'), (1500.2, 'banana')]
#Descending order
[(1500.2, 'banana'), (789.0, 'pineapple'), (500.1, 'apple'), (1.0, 'orange')]

Just learned relevant skill from Python for Everybody.

You may use a temporary list to help you to sort the dictionary:

#Assume dictionary to be:
d = {'apple': 500.1, 'banana': 1500.2, 'orange': 1.0, 'pineapple': 789.0}

# create a temporary list
tmp = []

# iterate through the dictionary and append each tuple into the temporary list 
for key, value in d.items():
    tmptuple = (value, key)
    tmp.append(tmptuple)

# sort the list in ascending order
tmp = sorted(tmp)

print (tmp)

If you want to sort the list in descending order, simply change the original sorting line to:

tmp = sorted(tmp, reverse=True)

Using list comprehension, the one liner would be:

#Assuming the dictionary looks like
d = {'apple': 500.1, 'banana': 1500.2, 'orange': 1.0, 'pineapple': 789.0}
#One liner for sorting in ascending order
print (sorted([(v, k) for k, v in d.items()]))
#One liner for sorting in descending order
print (sorted([(v, k) for k, v in d.items()], reverse=True))

Sample Output:

#Asending order
[(1.0, 'orange'), (500.1, 'apple'), (789.0, 'pineapple'), (1500.2, 'banana')]
#Descending order
[(1500.2, 'banana'), (789.0, 'pineapple'), (500.1, 'apple'), (1.0, 'orange')]

回答 26

遍历字典并按其值降序对其进行排序:

$ python --version
Python 3.2.2

$ cat sort_dict_by_val_desc.py 
dictionary = dict(siis = 1, sana = 2, joka = 3, tuli = 4, aina = 5)
for word in sorted(dictionary, key=dictionary.get, reverse=True):
  print(word, dictionary[word])

$ python sort_dict_by_val_desc.py 
aina 5
tuli 4
joka 3
sana 2
siis 1

Iterate through a dict and sort it by its values in descending order:

$ python --version
Python 3.2.2

$ cat sort_dict_by_val_desc.py 
dictionary = dict(siis = 1, sana = 2, joka = 3, tuli = 4, aina = 5)
for word in sorted(dictionary, key=dictionary.get, reverse=True):
  print(word, dictionary[word])

$ python sort_dict_by_val_desc.py 
aina 5
tuli 4
joka 3
sana 2
siis 1

回答 27

如果您的值是整数,并且使用的是Python 2.7或更高版本,则可以使用collections.Counter代替dict。该most_common方法将为您提供所有项目,并按值排序。

If your values are integers, and you use Python 2.7 or newer, you can use collections.Counter instead of dict. The most_common method will give you all items, sorted by the value.


回答 28

为了完整起见,我发布了一个使用heapq的解决方案。请注意,此方法适用于数值和非数值

>>> x = {1: 2, 3: 4, 4:3, 2:1, 0:0}
>>> x_items = x.items()
>>> heapq.heapify(x_items)
>>> #To sort in reverse order
>>> heapq.nlargest(len(x_items),x_items, operator.itemgetter(1))
[(3, 4), (4, 3), (1, 2), (2, 1), (0, 0)]
>>> #To sort in ascending order
>>> heapq.nsmallest(len(x_items),x_items, operator.itemgetter(1))
[(0, 0), (2, 1), (1, 2), (4, 3), (3, 4)]

For the sake of completeness, I am posting a solution using heapq. Note, this method will work for both numeric and non-numeric values

>>> x = {1: 2, 3: 4, 4:3, 2:1, 0:0}
>>> x_items = x.items()
>>> heapq.heapify(x_items)
>>> #To sort in reverse order
>>> heapq.nlargest(len(x_items),x_items, operator.itemgetter(1))
[(3, 4), (4, 3), (1, 2), (2, 1), (0, 0)]
>>> #To sort in ascending order
>>> heapq.nsmallest(len(x_items),x_items, operator.itemgetter(1))
[(0, 0), (2, 1), (1, 2), (4, 3), (3, 4)]

回答 29

由于需要保持与旧版本Python的向后兼容性,我认为OrderedDict解决方案非常不明智。您需要适用于Python 2.7和更早版本的工具。

但是,另一个答案中提到的集合解决方案绝对是极好的,因为您可以重新训练键和值之间的联系,这对于字典而言极为重要。

我不同意另一个答案中提出的第一选择,因为它会丢掉钥匙。

我使用了上面提到的解决方案(如下所示的代码),并保留了对键和值的访问,在我的情况下,排序是在值上进行的,但重要的是在对值进行排序之后对键进行排序。

from collections import Counter

x = {'hello':1, 'python':5, 'world':3}
c=Counter(x)
print c.most_common()


>> [('python', 5), ('world', 3), ('hello', 1)]

Because of requirements to retain backward compatability with older versions of Python I think the OrderedDict solution is very unwise. You want something that works with Python 2.7 and older versions.

But the collections solution mentioned in another answer is absolutely superb, because you retrain a connection between the key and value which in the case of dictionaries is extremely important.

I don’t agree with the number one choice presented in another answer, because it throws away the keys.

I used the solution mentioned above (code shown below) and retained access to both keys and values and in my case the ordering was on the values, but the importance was the ordering of the keys after ordering the values.

from collections import Counter

x = {'hello':1, 'python':5, 'world':3}
c=Counter(x)
print c.most_common()


>> [('python', 5), ('world', 3), ('hello', 1)]