Python中的键控集合?
Python中是否有任何等价物,即元素拥有(或动态生成)自己的键的集合 i、 e.这里的目标是避免将密钥存储在两个地方,因此字典不太理想(这就是问题所在)。我不太喜欢C#er,但我认为字典是你需要的 或者可能列出: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
您可以非常轻松地模拟:
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