Мне нужно иметь словарь, который может иметь одинаковые имена для некоторых ключей и возвращать список значений при ссылке на ключ в этом случае.
Например
print mydict['key']
[1,2,3,4,5,6]
Мне нужно иметь словарь, который может иметь одинаковые имена для некоторых ключей и возвращать список значений при ссылке на ключ в этом случае.
Например
print mydict['key']
[1,2,3,4,5,6]
Для согласованности вы должны иметь ключи карты словаря для списков (или наборов) значений, из которых некоторые могут быть пустыми. Для этого есть приятная идиома:
from collections import defaultdict
d = defaultdict(set)
d["key"].add(...)
(A defaultdict
похож на обычный словарь, но если отсутствует ключ, он вызывается аргументом, который вы передали, когда вы его создавали и использовали результат как значение по умолчанию. Таким образом, это автоматически создаст пустой набор значения, если вы запрашиваете ключ, который еще не присутствует.)
Если вам нужен объект, похожий на словарь (т.е. установить значение d["key"] = ...
), вы можете сделать следующее. Но это, вероятно, плохая идея, потому что она идет против обычного синтаксиса Python и, скорее всего, вернется и укусит вас позже. Особенно, если кто-то еще должен поддерживать ваш код.
class Multidict(defaultdict):
def __init__(self):
super(Multidict, self).__init__(set)
def __setitem__(self, key, value):
self[key].add(value)
Я не тестировал это.
Вы также можете попробовать paste.util.multidict.MultiDict
$ easy_install Paste
Тогда:
from paste.util.multidict import MultiDict
d = MultiDict()
d.add('a', 1)
d.add('a', 2)
d.add('b', 3)
d.mixed()
>>> {'a': [1, 2], 'b': 3}
d.getall('a')
>>> [1, 2]
d.getall('b')
>>> [3]
Веб-фреймворки, такие как Pylons, используют эту библиотеку для обработки строковых/почтовых данных HTTP-запроса, которые могут иметь одноименные ключи.
Вы можете использовать:
myDict = {'key': []}
Затем во время выполнения:
if newKey in myDict:
myDict[newKey].append(value)
else:
myDict[newKey] = [value]
Отредактировано в соответствии с комментарием @Ben:
myDict = {}
myDict.setdefault(newKey, []).append(value)
Я не удовлетворен всеми предлагаемыми решениями, так что это мое решение. Это для Python 3. Код ниже.
(код ниже)
>>> a = MultiDict({0: [0]})
>>> a
MultiDict({0: [0]})
>>> a[0] = (1, 7)
>>> a
MultiDict({0: [1, 7]})
>>> a.add(0, 2)
>>> a
MultiDict({0: [1, 7, 2]})
>>> a.add(1, 2)
>>> a
MultiDict({0: [1, 7, 2], 1: [2]})
>>> a.getfirst(0)
1
>>> a.getfirst(3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 61, in getfirst
File "<stdin>", line 17, in __getitem__
KeyError: 3
>>> len(a)
2
>>> tuple(a.items())
((0, [1, 7, 2]), (1, [2]))
>>> tuple(a.values())
([1, 7, 2], [2])
>>> a.get(0)
[1, 7, 2]
>>> tuple(a.multiitems())
((0, 1), (0, 7), (0, 2), (1, 2))
>>> tuple(a.multikeys())
(0, 0, 0, 1)
>>> tuple(a.multivalues())
(1, 7, 2, 2)
>>> a.remove(0, 1)
>>> a
MultiDict({0: [7, 2], 1: [2]})
>>> a.remove(3, 5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 53, in remove
File "<stdin>", line 17, in __getitem__
KeyError: 3
>>> a.remove(0, 5)
Traceback (most recent call last):
File "<stdin>", line 53, in remove
ValueError: list.remove(x): x not in list
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 56, in remove
ValueError: No element with value 5 for key 0
>>> b = MultiDict({0: [7, 2], 1: [2]})
>>> b == a
True
>>> c = MultiDict(a)
>>> c
MultiDict({0: [7, 2], 1: [2]})
>>> d = MultiDict({0: 0})
Traceback (most recent call last):
File "<stdin>", line 30, in __init__
TypeError: 'int' object is not iterable
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 33, in __init__
TypeError: Values must be iterables, found 'int' for key 0
>>> a.pop(0)
[7, 2]
>>> a
MultiDict({1: [2]})
>>> c.popitem()
(0, [7, 2])
>>> c.setdefault(0, [1])
[1]
>>> c
MultiDict({0: [1], 1: [2]})
>>> c.setdefault(0, [2])
[1]
>>> c
MultiDict({0: [1], 1: [2]})
>>> c.setdefault(3)
[]
>>> c
MultiDict({0: [1], 1: [2], 3: []})
>>> c.getfirst(3)
Traceback (most recent call last):
File "<stdin>", line 61, in getfirst
IndexError: list index out of range
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 63, in getfirst
IndexError: No values in key 3
>>> c.clear()
>>> c
MultiDict({})
>>> c.update(b)
>>> c
MultiDict({0: [7, 2], 1: [2]})
>>> d = c.copy()
>>> d == c
True
>>> id(d) == id(c)
False
>>> MultiDict.fromkeys((0, 1), [5])
MultiDict({0: [5], 1: [5]})
>>> MultiDict.fromkeys((0, 1))
MultiDict({0: [], 1: []})
from collections.abc import MutableMapping
class MultiDict(MutableMapping):
@classmethod
def fromkeys(cls, seq, value=None, *args, **kwargs):
if value is None:
v = []
else:
v = value
return MultiDict(dict.fromkeys(seq, v, *args, **kwargs))
def __setitem__(self, k, v):
self._dict[k] = list(v)
def __getitem__(self, k):
return self._dict[k]
def __iter__(self):
for k in self._dict:
yield k
def __init__(self, *args, **kwargs):
self._dict = dict(*args, **kwargs)
for k, v in self._dict.items():
try:
self._dict[k] = list(v)
except TypeError:
err_str = "Values must be iterables, found '{t}' for key {k}"
raise TypeError(err_str.format(k=k, t=type(v).__name__))
def __delitem__(self, k):
del self._dict[k]
def __len__(self):
return len(self._dict)
def add(self, k, v):
if not k in self:
self[k] = []
self[k].append(v)
def remove(self, k, v):
try:
self[k].remove(v)
except ValueError:
err_str = "No element with value {v} for key {k}"
raise ValueError(err_str.format(v=v, k=k))
def getfirst(self, k):
try:
res = self[k][0]
except IndexError:
raise IndexError("No values in key {k}".format(k=k))
return self[k][0]
def multiitems(self):
for k, v in self.items():
for vv in v:
yield (k, vv)
def multikeys(self):
for k, v in self.items():
for vv in v:
yield k
def multivalues(self):
for v in self.values():
for vv in v:
yield vv
def setdefault(self, k, default=None):
if default is None:
def_val = []
else:
def_val = default
if k not in self:
self[k] = def_val
return self[k]
def copy(self):
return MultiDict(self)
def __repr__(self):
body_str = ""
for k, v in self.items():
body_str += "{k}: {v}, ".format(k=repr(k), v=repr(v))
if body_str:
body_str_true = body_str[:-2]
else:
body_str_true = body_str
return "MultiDict({{{body}}})".format(body=body_str_true)
Для простоты конструктор совпадает с dict
. Все значения, переданные конструктору или назначенные непосредственно ключу, должны быть итерабельными.
Все значения my MultiDict
являются списками, даже если значение равно только одному. Это делается для того, чтобы избежать путаницы.
Я добавил также метод remove
для удаления одной записи из MultiDict
. Кроме того, я добавил multiitems
, который перебирает пару (ключ, значение) по всем значениям словаря. multikeys
и multivalues
похожи.
Вы также можете использовать aiohttp, WebOp или Werkzeug реализация MultiDict.
def toMultiDict(items):
def insertMulti(d, kv):
k, v = kv
d.setdefault(k, []).append(v)
return d
return reduce(insertMulti, [{}] + items)
должен создать dict из ключа в список значений:
In [28]: toMultiDict(zip([1,2,1], [4,5,6]))
Out[28]: {1: [4, 6], 2: [5]}
Я не мог поместить insertMulti в лямбда, потому что лямбда должна снова вернуть dict.
Это идеальное место для использования объекта defaultdict из библиотеки коллекций
from collections import defaultdict
mydict = defaultdict(set)
mydict['key'] += set([1,2,3,4])
mydict['key'] += set([4,5,6])
print(mydict['key'])
возвращает [1,2,3,4,5,6]
В случае ссылки на ключ, который не был неявно назначен, возвращается пустой набор.
print(mydict['bad_key'])
возвращает []
Использование setdefault в dict из стандартной библиотеки потребует значительного изменения вашего синтаксиса при назначении значений и может стать довольно грязным. Я никогда не использовал Multidict, но он также выглядит значительным изменением в способах создания заданий. Используя этот метод, вы просто предполагаете, что уже может быть значение, связанное с этим ключом в словаре, и слегка изменить ваш оператор присваивания с помощью оператора "+ =" при назначении значений ключа.
FYI. Я большой поклонник использования NoneType в качестве значения по умолчанию, которое приводит к любому доступу к недопустимому ключу, возвращающему None. В большинстве случаев это ведет себя правильно, включая итерационные и json-дампы, но для вашей конкретной потребности по умолчанию должен быть установлен тип, если вы не хотите включать дубликаты значений, хранящихся в ключе. Затем используйте список. Фактически, в любое время, когда у вас есть гомогенный словарь, значение по умолчанию должно быть такого типа.
mydict = defaultdict(lambda: None)