Obecnie jest w potrzebie klasy kontenerowej Pythona o podobnej funkcjonalności, takiej jak wbudowany typ dict. Zasadniczo to, czego potrzebuję, jest słownikiem, gdzie dowolna liczba klawiszy obok klucza podstawowego, która mapa do tej samej wartości. Jednak kiedy nad nim należy go iterować tylko nad parami (primary_key, value) i tylko podstawowym kluczem, jeśli zostanie żądana lista kluczy.

Jeśli został już zaimplementowany, wolałbym, wolę nie zniszczyć koła. Czy więc jest już moduł zapewniający taki kontener? Jeśli nie, będę go wdrożyć.

4
datenwolf 28 listopad 2011, 22:12

3 odpowiedzi

Najlepsza odpowiedź

Oto szybka realizacja:

class MultipleKeyDict(dict):
    __slots__ = ["_primary_keys"]
    def __init__(self, arg=None, **kwargs):
        self._primary_keys = {}
        self.update(arg, **kwargs)
    def __setitem__(self, key, value):
        super(MultipleKeyDict, self).__setitem__(key, value)
        self._primary_keys.setdefault(value, key)
    def __delitem__(self, key):
        value = self[key]
        super(MultipleKeyDict, self).__delitem__(key)
        if self._primary_keys[value] == key:
            del self._primary_keys[value]
            for k, v in super(MultipleKeyDict, self).iteritems():
                if v == value:
                    self._primary_keys[value] = k
                    break
    def __iter__(self):
        return self.iterkeys()
    def update(self, arg=None, **kwargs):
        if arg is not None:
            if isinstance(arg, collections.Mapping):
                for k in arg:
                    self[k] = arg[k]
            else:
                for k, v in arg:
                    self[k] = v
        for k in kwargs:
            self[k] = kwargs[k]
    def clear(self):
        super(MultipleKeyDict, self).clear()
        self._primary_keys.clear()
    def iteritems(self):
        for v, k in self._primary_keys.iteritems():
            yield k, v
    def items(self):
        return list(self.iteritems())
    def itervalues(self):
        return self._primary_keys.iterkeys()
    def values(self):
        return self._primary_keys.keys()
    def iterkeys(self):
        return self._primary_keys.itervalues()
    def keys(self):
        return self._primary_keys.values()

Jedynym bitkiem jest to, że musi szukać całego dyktatu na wypadek, gdyby klucz podstawowy zostanie usunięty.

Pomijam copy(), pop(), popitem() i setdefault(). Jeśli ich potrzebujesz, musisz je wdrożyć.

2
Sven Marnach 2 grudzień 2011, 15:14

Najprostszym i najłatwiejszym rozwiązaniem byłoby użycie dwóch słowników, z których jeden z nich mapy wtórne klucze do klucza podstawowego. Jeśli z jakiegoś powodu potrzebujesz odwrotnej mapowania, które mogłyby być zawarte w słowniku podstawowym.

sec = {'one': 'blue', 'two': 'red', 'three': 'blue',   # seconary keys
       'blue': 'blue', 'red': 'red'}                   # include identity mapping for primaries
dict = {'blue': ('doll', '$9.43', ('one', 'three')),
        'red':  ('truck', '$14.99', ('two',)) }

record = dict[sec['two']]
print('Toy=', record[0], 'Price=', record[1])
1
Dave 28 listopad 2011, 19:06

Teraz jest pakiet wielokrotnego słownika Pythona.
https://pypi.python.org/pypi/multi_key_dict/1.0.2 .

Z linku:

from multi_key_dict import multi_key_dict

k = multi_key_dict()
k[1000, 'kilo', 'k'] = 'kilo (x1000)'

print k[1000] # will print 'kilo (x1000)'
print k['k'] # will also print 'kilo (x1000)'

# the same way objects can be updated, deleted:
# and if an object is updated using one key, the new value will
# be accessible using any other key, e.g. for example above:
k['kilo'] = 'kilo'
print k[1000] # will now print 'kilo' as value was updated
0
sickgemini 12 wrzesień 2013, 00:57