Python中的键控集合?

Python中的键控集合?,python,keyedcollection,Python,Keyedcollection,Python中是否有任何等价物,即元素拥有(或动态生成)自己的键的集合 i、 e.这里的目标是避免将密钥存储在两个地方,因此字典不太理想(这就是问题所在)。我不太喜欢C#er,但我认为字典是你需要的 或者可能列出: 您可以非常轻松地模拟: class KeyedObject(object): def get_key(self): raise NotImplementedError("You must subclass this before you can use i

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

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

我不太喜欢C#er,但我认为字典是你需要的

或者可能列出:


您可以非常轻松地模拟:

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
基于某些对象属性返回有效键)。

为什么不简单地使用
dict
?如果密钥已经存在,则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

我不确定这是否是你的意思,但当你添加到字典中时,它会创建自己的键

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'}
@迈赫达德说:

因为在语义上,它没有那么多意义。当一个物体 知道它的关键,把它放在字典里是没有意义的——它是 不是键值对。这更像是一个语义问题 否则

有了这个约束,Python中没有任何东西可以满足您的需求。我建议您使用dict,不要担心语义上的这种详细程度@Gabi Purcaru的回答说明了如何使用所需的接口创建对象。为什么要为它的内部工作方式而烦恼呢

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

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


考虑到您的限制,每个试图使用
dict
实现您想要的东西的人都是找错人了。相反,您应该编写一个
列表
子类来覆盖
\uuu getitem\uuu
,以提供所需的行为。我编写了它,因此它首先尝试通过索引获取所需的项,然后返回到通过所包含对象的
属性搜索项。(如果对象需要动态确定,则这可能是一个属性。)

如果你不想在某个地方复制某个东西,就无法避免线性搜索;如果不允许C#实现使用字典存储密钥,我相信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)

您可能还希望以类似的方式覆盖
\uuuuu contains\uuuuu
,这样您就可以在kc…中的“key”时说
。如果您想让它更像一个
dict
,您还可以实现
keys()
等等。它们同样效率低下,但您将拥有一个类似于
dict
的API,它也可以像列表一样工作。

那么
set()如何?元素可以有自己的k

来更详细地说明@Gabi Purcaru的答案中已经正确的答案,这里是一个与Gabi one的答案相同的类,但也检查键和值的正确给定类型(如.net KeyedCollection的TKey和TValue)


我试图避免将密钥存储两次,但这样做行不通:\@Mehrdad:这不会存储两次密钥,它会存储对密钥的两个引用。如果你有一个大字符串作为键,那么这个字符串在内存中只存在一次。@Ned:是的,我知道它在存储一个引用,但它存储了两次引用。我不仅不喜欢重复,更重要的是,请看我对你另一条评论的评论。@Mehrdad:记忆通常是丰富的(直到没有)。您的用户更喜欢哪一个:一个运行速度快的程序,因为它使用适合手头任务的数据结构,还是一个运行速度慢但内部数据结构中没有不必要的重复的程序?@kindall:这不是内存问题,而是可读性和语义问题。如果我必须存储某个东西两次,那么我必须跟踪它两次,这会使阅读变得更加困难。在这里担心记忆有点过分;这不是一个问题。你能说更多关于为什么你不想有键和值吗?Python通常只引用同一个对象作为键和值,因此不会产生两倍的内存。@Ned:因为从语义上讲,它没有多大意义。当一个对象知道它的键时,把它放在字典里是没有意义的——它不是一个键值对。这更像是一个语义问题,而不是其他问题。(还有一个问题是,
KeyedCollection
也可以通过序号进行索引,但我在这里并不特别需要它。尽管它在其他情况下很有用。)它可以,但是(1)它可以被禁用,(2)它仍然允许通过序号进行访问。@Mehrdad:请拿定主意。您说过您不关心序号的访问。我说过在这种情况下不关心它,但我通常关心它(即,大多数其他时候,当我需要KeyedCollection时)。但也要注意第(1)点。好的,我们正在构建KeyedCollection的Python实现,我们也可以添加索引查找,但请记住:这将需要存储对该值的另一个引用。感谢您提供的信息!虽然我可以手工实现它,但我想知道是否有内置的东西,这样我就不必定义/in了
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