7

是否有任何等效于 Python 中的KeyedCollection,即元素具有(或动态生成)自己的键的集合?

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

4

8 回答 8

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

现在您可以使用 aKeyedDict而不是dict带有子类KeyedObject(其中get_key返回基于某些对象属性的有效键)。

于 2011-07-21T18:14:32.083 回答
2

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

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

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()等等。它们同样效率低下,但您将拥有像 a 这样的 API dict,它也像列表一样工作。

于 2011-07-21T23:43:51.543 回答
1

我不是 C#'er,但我认为字典是你需要的。

http://docs.python.org/tutorial/datastructures.html#dictionaries

http://docs.python.org/tutorial/datastructures.html

或者也许列出:

http://docs.python.org/library/functions.html#list

于 2011-07-21T18:07:28.743 回答
1

@Mehrdad 说:

因为从语义上讲,它没有多大意义。当一个对象知道它的键时,把它放在字典中是没有意义的——它不是键值对。这更像是一个语义问题,而不是其他任何事情。

有了这个约束,Python 中就没有什么可以做你想做的了。我建议您使用 dict 而不必担心语义上的这种详细程度。@Gabi Purcaru 的回答显示了如何使用所需的界面创建对象。为什么要为它在内部的工作方式而烦恼?

可能是 C# 的 KeyedCollection 在幕后做着同样的事情:向对象询问其密钥,然后存储该密钥以便快速访问。事实上,从文档中:

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

于 2011-07-21T23:12:25.797 回答
0

I'm not sure if this is what you meant, but this dictionary will create it's own keys as you add to it...

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'}
于 2011-07-21T21:35:50.820 回答
0

为什么不简单地使用 a dict?如果 key 已经存在,则在 dict 中将使用对 key 的引用;它不会被毫无意义地复制。

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
于 2011-07-21T18:43:07.913 回答
0

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

于 2011-07-22T03:56:17.297 回答
0

为了更详细地了解@Gabi Purcaru 的答案中已经正确的答案,这里有一个与 gabi one's 一样的类,但它也检查键和值上的正确给定类型(作为 .net 的 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
于 2017-12-14T15:17:41.140 回答