Python создает собственный точный вид подмножества словаря - PullRequest
5 голосов
/ 17 февраля 2012

Как свидетельствуют многие вопросы по этой теме в SO, взятие фрагмента словаря является довольно распространенной задачей, с довольно хорошим решением:

{k:v for k,v in dict.viewitems() if some_test(k,v)}

Но это создает новый словарь с собственными сопоставлениями. Для многих операций было бы хорошо иметь неизменный вид исходного dict (то есть он не поддерживает операции присваивания или удаления в представлении). Реализация такого типа, вероятно, проста, но нехорошо иметь распространение локальных служебных классов.

Итак, мой вопрос: существует ли встроенный способ получения такого "представления подмножества"? Или есть сторонняя библиотека (желательно доступная через PyPi), которая обеспечивает хорошую реализацию такой утилиты?

Ответы [ 3 ]

4 голосов
/ 19 июля 2012

Кажется, что нет встроенного способа получить представление в словаре. Самый простой обходной путь - подход Йохена. Я немного адаптировал его код, чтобы он работал для моих целей:

from collections import MutableMapping

class DictView(MutableMapping):
    def __init__(self, source, valid_keys):
        self.source, self.valid_keys = source, valid_keys

    def __getitem__(self, key):
        if key in self.valid_keys:
            return self.source[key]
        else:
            raise KeyError(key)

    def __len__(self):
        return len(self.valid_keys)

    def __iter__(self):
        for key in self.valid_keys:
            yield key

    def __setitem__(self, key, value):
        if key in self.valid_keys:
            self.source[key] = value
        else:
            raise KeyError(key)

    def __delitem__(self, key):
        self.valid_keys.remove(key)

d = dict(a=1, b=2, c=3)
valid_keys = ['a', 'c']
d2 = DictView(d, valid_keys)
d2['a'] = -1  # overwrite element 'a' in source dictionary
print d  # prints {'a': -1, 'c': 3, 'b': 2}

Таким образом, d2 ведет себя как словарь во всех аспектах, кроме печати, из-за другого метода __repr__(). Наследование от dict для получения __repr__() потребует повторной реализации каждого метода, как это делается для collections.OrderedDict. Если кто-то хочет только чтение только для чтения, он может наследовать от collections.Mapping и сохранить реализацию __setitem__() и __delitem__(). DictView полезен для выбора параметров из self.__dict__ и передачи их в компактной форме.

4 голосов
/ 17 февраля 2012

Это довольно легко реализовать:

from collections import Mapping
class FilteredItems(Mapping):
    def __init__(self, source, filter):
        self.source = source
        self.p = filter

    def __getitem__(self, key):
        x = self.source[key]
        if self.p(key,x):
            return key,x
        else:
            raise KeyError(key)


d2 = FilteredItems(d, some_test)
2 голосов
/ 17 февраля 2012

Чтобы прояснить семантику, вы думаете о чем-то вроде этого:?

class FilteredDictView:
    def __init__(self, base_dict, test):
        self._base_dict = base_dict
        self._test = test
    def __getitem__(self, key):
        value = self._base_dict[key] # might throw KeyError
        if not self._test(key,value):
            throw KeyError(key)
        return value
    # ... implement remaining dict-like-methods ...

Если так, то я не знаю ни одного такого стороннего класса. Если вы хотите немного упростить реализацию оставшихся методов, вы можете посмотреть на использование «UserDict» в качестве базового класса, который по сути является просто оболочкой для dict (атрибут «UserDict.data» используется для хранения упакованного dict). .

...