Чи є в Python реалізація `` багатокарти ''?


75

Я новачок у Python, і я знайомий з реалізацією Multimaps в інших мовах . Чи має Python таку структуру даних, вбудовану або доступну в загальновживаній бібліотеці?

Щоб проілюструвати, що я маю на увазі під "багатокарткою":

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

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

3
@ccfenix, я додав приклад того, що, на мою думку, ти хотів. Якщо це неправильно, відредагуйте, щоб приклад був правильним. Приклад допомагає людям відповісти на ваше запитання; вони повинні знати, що ви шукаєте.
steveha

так, саме те, що я хочу, дякую Steveha!
Джеймс Бонд,

code.activestate.com/recipes/…, здається, реалізує синтаксис, за яким ви
переслідуєте

1
Використання, a[1] = 'b'щоб означати додавання до [1] , буде незрозумілим для людей, які читають або підтримують цей код. Я б рекомендував вам не робити цього в Python.
Poolie

Відповіді:


126

Такого немає в стандартній бібліотеці. Ви можете використовувати defaultdict:

>>> 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']

(Замість listвас, можливо, вам захочеться скористатися set, і в цьому випадку ви б зателефонували .addзамість цього .append.)


Осторонь : подивіться на ці два рядки, які ви писали:

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

Здається, це вказує на те, що ви хочете, щоб вираз a[1]дорівнював двом різним значенням. Це неможливо для словників, оскільки їх ключі унікальні, і кожен з них пов’язаний з одним значенням. Проте ви можете витягти все значення у списку, пов’язаному з даним ключем, одне за одним. Для цього можна використовувати iterнаступні послідовні дзвінки до next. Або ви можете просто використовувати дві петлі:

>>> 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'

9

Тільки для майбутніх відвідувачів. В даний час існує реалізація Python Multimap. Це доступно через pypi


2
"Визначальною якістю цього проекту проти інших є те, що значення, що відображаються одним і тим же ключем, не впорядковуються разом". Чим це відрізняється від використання defaultdict(set)?
Шукласваг,

У вашому посиланні не написано "замовлено", а "згруповано". Отже, це не набір, оскільки один і той же елемент можна вставити двічі. Я думаю, це поводиться більше як відсортований список.
Еял

4

Stephan202 має правильну відповідь, використовуйте defaultdict. Але якщо ви хочете щось з інтерфейсом мультимапи C ++ STL і набагато гіршу продуктивність, ви можете зробити це:

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

Тепер, коли ви переглядаєте multimap, ви отримаєте пари, як у файлі std::multimap. На жаль, це означає, що ваш код циклу почне виглядати так само потворно, як 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

Таким чином, defaultdictце дійсно круто і використовує силу python, і ви повинні використовувати його.


4

Ви можете взяти список кортежів, а потім відсортувати їх так, ніби це мультимапа.

listAsMultimap=[]

Давайте додамо деякі елементи (кортежі):

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

Тепер сортуй.

listAsMultimap=sorted(listAsMultimap)

Після його роздрукування ви отримаєте:

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

Це означає, що він працює як Multimap!

Зверніть увагу, що як і мультимап, тут також значення сортуються за зростанням, якщо ключі однакові (для ключа = 2, "b" стоїть перед "c", хоча ми не додавали їх у такому порядку).

Якщо ви хочете отримати їх у порядку зменшення, просто змініть функцію sorted () таким чином:

listAsMultimap=sorted(listAsMultimap,reverse=True)

І після того, як ви отримаєте такий вивід:

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

Подібним чином тут значення знаходяться у порядку зменшення, якщо ключі однакові.


Це НЕ те саме, що мультимапа, де витрати O (1). Вище - O (NlogN).
user48956

@ user48956, я думаю, вас плутає unordered_multimap та multimap. Multimap має суміш O (NlogN) та NOT O (1). І вище реалізація призначена для Multimap, а НЕ для unordered_multimap, яка використовує хешування для зменшення часової складності і має постійну середню складність часу в середньому. Знайдіть відмінності тут: ( en.cppreference.com/w/cpp/container/multimap ) і тут: ( cplusplus.com/reference/unordered_map/unordered_multimap )
hafiz031

3

Стандартний спосіб писати це на Python - це дикт, елементами якого є кожен listабо set. Як каже stephan202 , ви можете дещо автоматизувати це за допомогою дефолту, але це не потрібно.

Іншими словами, я б переклав ваш код на

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

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

Чому голоси проти? Тому що це мало схоже на дикт? Я вважаю, що розглядати a[1] = 'b'як додавання, а не замінюючи a[1], є більше заплутаним, ніж корисним.
Poolie

2
Я не голосував проти, але я думаю, що ваша пропозиція нічого не додає до обговорення, що може пояснити негативну реакцію. Ви, звичайно, можете використовувати дикт ключів до списків значень, але це ручне втілення дратує, повторюється і дещо схильне до помилок. Мультимапа, як Multimap Гуави для Java, є не що інше, як картою списків, але надзвичайно зручною саме тому, що приховує таку реалізацію. Ваша пропозиція є точною, але їй не вистачає будь-якої зручності.
dimo414

4
Суть, про яку я намагався сказати, а не інші, полягає в тому, що використання мультимапи не є пітонічним. Ідіоматичним способом є дикт множин.
Poolie

Мені подобається обговорення пітоніки. Виходячи з Java та Perl, я підозрюю, що стурбованість @ dimo414 пов’язана із створенням списку під час додавання нового ключа. Відповідь могла б розширитися на те, що в цьому відношенні є пітонічним.
Кріс,

... пішов і детальніше розглянув вирок за замовчуванням і сподобався як рішення. Я гадаю, голоси проти - тому, що ви не зверталися до створення списку, який для вас робить дефолт.
Кріс,

3

Або підклас 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)

2
Однак це не буде вести себе точно так само, як докт. stackoverflow.com/questions/3387691 / ...
johncip

2

Наразі в стандартних бібліотеках Python немає багатокарт.

WebOb має MultiDict клас використовується для представлення значень форми HTML, і він використовується декількома фреймворками Python, тому реалізація перевірена боєм.

Werkzeug також має клас MultiDict , і з тієї ж причини.


-4

Я не чітко розумію семантику вашого прикладу

a[1] = 'a'
a[1] = 'b' #??

Чи другий рядок a[1] = 'b'повинен замінити елемент у [1]. Якщо так, то вам потрібно скористатися словником. Якщо ні - потрібно використовувати словник списків (як уже пропонувалось)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.