Python 中有“ multimap”实现吗?

我是 Python 的新手,并且熟悉 其他 语言多地图的实现。Python 是否内置了这样的数据结构,或者在常用的库中可用?

为了说明我所说的“多重地图”的含义:

a = multidict()
a[1] = 'a'
a[1] = 'b'
a[2] = 'c'


print(a[1])  # prints: ['a', 'b']
print(a[2])  # prints: ['c']
45525 次浏览

Such a thing is not present in the standard library. You can use a defaultdict though:

>>> from collections import defaultdict
>>> md = defaultdict(list)
>>> md[1].append('a')
>>> md[1].append('b')
>>> md[2].append('c')
>>> md[1]
['a', 'b']
>>> md[2]
['c']

(Instead of list you may want to use set, in which case you'd call .add instead of .append.)


As an aside: look at these two lines you wrote:

a[1] = 'a'
a[1] = 'b'

This seems to indicate that you want the expression a[1] to be equal to two distinct values. This is not possible with dictionaries because their keys are unique and each of them is associated with a single value. What you can do, however, is extract all values inside the list associated with a given key, one by one. You can use iter followed by successive calls to next for that. Or you can just use two loops:

>>> for k, v in md.items():
...     for w in v:
...         print("md[%d] = '%s'" % (k, w))
...
md[1] = 'a'
md[1] = 'b'
md[2] = 'c'

There is no multi-map in the Python standard libs currently.

WebOb has a MultiDict class used to represent HTML form values, and it is used by a few Python Web frameworks, so the implementation is battle tested.

Werkzeug also has a MultiDict class, and for the same reason.

The standard way to write this in Python is with a dict whose elements are each a list or set. As stephan202 says, you can somewhat automate this with a defaultdict, but you don't have to.

In other words I would translate your code to

a = dict()
a[1] = ['a', 'b']
a[2] = ['c']


print(a[1])  # prints: ['a', 'b']
print(a[2])  # prints: ['c']

Stephan202 has the right answer, use defaultdict. But if you want something with the interface of C++ STL multimap and much worse performance, you can do this:

multimap = []
multimap.append( (3,'a') )
multimap.append( (2,'x') )
multimap.append( (3,'b') )
multimap.sort()

Now when you iterate through multimap, you'll get pairs like you would in a std::multimap. Unfortunately, that means your loop code will start to look as ugly as C++.

def multimap_iter(multimap,minkey,maxkey=None):
maxkey = minkey if (maxkey is None) else maxkey
for k,v in multimap:
if k<minkey: continue
if k>maxkey: break
yield k,v


# this will print 'a','b'
for k,v in multimap_iter(multimap,3,3):
print v

In summary, defaultdict is really cool and leverages the power of python and you should use it.

Or subclass dict:

class Multimap(dict):
def __setitem__(self, key, value):
if key not in self:
dict.__setitem__(self, key, [value])  # call super method to avoid recursion
else
self[key].append(value)

Just for future visitors. Currently there is a python implementation of Multimap. It's available via pypi

You can take list of tuples and than can sort them as if it was a multimap.

listAsMultimap=[]

Let's append some elements (tuples):

listAsMultimap.append((1,'a'))
listAsMultimap.append((2,'c'))
listAsMultimap.append((3,'d'))
listAsMultimap.append((2,'b'))
listAsMultimap.append((5,'e'))
listAsMultimap.append((4,'d'))

Now sort it.

listAsMultimap=sorted(listAsMultimap)

After printing it you will get:

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

That means it is working as a Multimap!

Please note that like multimap here values are also sorted in ascending order if the keys are the same (for key=2, 'b' comes before 'c' although we didn't append them in this order.)

If you want to get them in descending order just change the sorted() function like this:

listAsMultimap=sorted(listAsMultimap,reverse=True)

And after you will get output like this:

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

Similarly here values are in descending order if the keys are the same.