Python中的键控收集?

时间:2011-07-21 18:02:13

标签: python keyedcollection

Python中是否有KeyedCollection的等价物,即元素拥有(或动态生成)自己的键的集合?

即。这里的目标是避免将密钥存储在两个地方,因此字典不太理想(因此问题)。

8 个答案:

答案 0 :(得分:3)

您可以非常轻松地模拟它:

class KeyedObject(object):
    def get_key(self):
        raise NotImplementedError("You must subclass this before you can use it.")

class KeyedDict(dict):
    def append(self, obj):
        self[obj.get_key()] = obj

现在,您可以使用KeyedDict代替dict,其子类为KeyedObject(其中get_key根据某些对象属性返回有效密钥。)

答案 1 :(得分:2)

鉴于你的约束,每个试图使用dict实现你正在寻找的东西的人正在咆哮错误的树。相反,您应该编写一个覆盖list的{​​{1}}子类来提供您想要的行为。我已经编写了它,因此它首先尝试通过索引获取所需的项目,然后回退到按所包含对象的__getitem__属性搜索项目。 (如果对象需要动态确定,这可能是属性。)

如果您不想在某处复制某些内容,则无法避免线性搜索;如果你不允许它使用字典来存储密钥,我确信C#实现完全一样。

key

您可能还希望以类似方式覆盖class KeyedCollection(list): def __getitem__(self, key): if isinstance(key, int) or isinstance(key, slice): return list.__getitem__(key) for item in self: if getattr(item, "key", 0) == key: return item raise KeyError('item with key `%s` not found' % key) ,以便您可以说__contains__。如果你想让它更像if "key" in kc...,你也可以实现dict等等。它们同样效率低下,但您将拥有类似keys()的API,它也可以像列表一样工作。

答案 2 :(得分:1)

答案 3 :(得分:1)

@Mehrdad说:

  

因为在语义上,它没有那么多意义。当一个对象   知道它的关键,把它放在字典中没有意义 - 就是这样   不是键值对。它更像是一个语义问题   其他

使用此约束,Python中没有任何内容可以执行您想要的操作。我建议你使用dict而不用担心语义上的这种详细程度。 @Gabi Purcaru的答案显示了如何使用所需的界面创建对象。为什么要对内部工作方式感到困扰?

可能是C#的KeyedCollection在幕后做了同样的事情:询问对象的密钥,然后存储密钥以便快速访问。实际上,来自文档:

  

默认情况下,KeyedCollection(Of TKey,TItem)包含查找   您可以使用Dictionary属性获取的字典。当一个   item被添加到项目密钥KeyedCollection(Of TKey,TItem)中   被提取一次并保存在查找字典中以便更快   搜索。通过指定字典来覆盖此行为   创建KeyedCollection时创建阈值(Of TKey,   TItem)。查找字典是第一次创建的数量   元素超过了这个阈值。如果指定-1作为阈值,   永远不会创建查找字典。

答案 4 :(得分:0)

为什么不简单地使用dict?如果密钥已存在,则将在字典中使用对密钥的引用;它不会毫无意义地重复。

class MyExample(object):
    def __init__(self, key, value):
        self.key = key
        self.value = value

m = MyExample("foo", "bar")
d = {}

d[m.key] = m

first_key = d.keys()[0]
first_key is m.key  # returns True

如果该密钥尚不存在,则会保存该密钥的副本,但我不认为这是一个问题。

def lame_hash(s):
    h = 0
    for ch in s:
        h ^= ord(ch)
    return h

d = {}
d[lame_hash(m.key)] = m
print d  # key value is 102 which is stored in the dict

lame_hash(m.key) in d  # returns True

答案 5 :(得分:0)

我不确定这是不是你的意思,但是当你添加它时,这本词典会创建它自己的键......

class KeyedCollection(dict):
    def __init__(self):
        self.current_key = 0
    def add(self, item):
        self[self.current_key] = item

abc = KeyedCollection()
abc.add('bob')
abc.add('jane')
>>> abc
{0: 'bob', 1: 'jane'}

答案 6 :(得分:0)

set()怎么样?元素可以有自己的k

答案 7 :(得分:0)

再详细一点,来自@Gabi Purcaru的答案已经正确答案了,这里有一个与gabi相同的课程,但是也要检查键上的正确给定类型value(作为.net KeyedCollection的TKey和TValue)。

class KeyedCollection(MutableMapping):
    """
    Provides the abstract base class for a collection (:class:`MutableMappinp`) whose keys are embedded in the values.
    """
    __metaclass__ = abc.ABCMeta
    _dict = None  # type: dict

    def __init__(self, seq={}):
        self._dict = dict(seq)

    @abc.abstractmethod
    def __is_type_key_correct__(self, key):
        """
        Returns: The type of keys in the collection
        """
        pass

    @abc.abstractmethod
    def __is_type_value_correct__(self, value):
        """
        Returns: The type of values in the collection
        """
        pass

    @abc.abstractmethod
    def get_key_for_item(self, value):
        """
        When implemented in a derivated class, extracts the key from the specified element.
        Args:
            value: the element from which to extract the key (of type specified by :meth:`type_value`)

        Returns: The key of specified element (of type specified by :meth:`type_key`)
        """
        pass

    def __assert_type_key(self, key, arg_name='key'):
        if not self.__is_type_key_correct__(key) :
            raise ValueError("{} type is not correct".format(arg_name))

    def __assert_type_value(self, value, arg_name='value'):
        if not self.__is_type_value_correct__(value) :
            raise ValueError("{} type is not correct".format(arg_name))

    def add(self, value):
        """
        Adds an object to the KeyedCollection.
        Args:
            value: The object to be added to the KeyedCollection (of type specified by :meth:`type_value`).
        """
        key = self.get_key_for_item(value)
        self._dict[key] = value

    # Implements abstract method __setitem__ from MutableMapping parent class
    def __setitem__(self, key, value):
        self.__assert_type_key(key)
        self.__assert_type_value(value)
        if value.get_key() != key:
            raise ValueError("provided key does not correspond to the given KeyedObject value")
        self._dict[key] = value

    # Implements abstract method __delitem__ from MutableMapping parent class
    def __delitem__(self, key):
        self.__assert_type_key(key)
        self._dict.pop(key)

    # Implements abstract method __getitem__ from MutableMapping parent class (Mapping base class)
    def __getitem__(self, key):
        self.__assert_type_key(key)
        return self._dict[key]

    # Implements abstract method __len__ from MutableMapping parent class (Sized mixin on Mapping base class)
    def __len__(self):
        return len(self._dict)

    # Implements abstract method __iter__ from MutableMapping parent class (Iterable mixin on Mapping base class)
    def __iter__(self):
        return iter(self._dict)
        pass

    # Implements abstract method __contains__ from MutableMapping parent class (Container mixin on Mapping base class)
    def __contains__(self, x):
        self.__assert_type_key(x, 'x')
        return x in self._dict