是否有任何等效于 Python 中的KeyedCollection,即元素具有(或动态生成)自己的键的集合?
即这里的目标是避免将密钥存储在两个地方,因此字典不太理想(因此提出了问题)。
是否有任何等效于 Python 中的KeyedCollection,即元素具有(或动态生成)自己的键的集合?
即这里的目标是避免将密钥存储在两个地方,因此字典不太理想(因此提出了问题)。
你可以很容易地模拟:
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
返回基于某些对象属性的有效键)。
鉴于您的限制,每个尝试使用 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
,它也像列表一样工作。
我不是 C#'er,但我认为字典是你需要的。
http://docs.python.org/tutorial/datastructures.html#dictionaries
http://docs.python.org/tutorial/datastructures.html
或者也许列出:
@Mehrdad 说:
因为从语义上讲,它没有多大意义。当一个对象知道它的键时,把它放在字典中是没有意义的——它不是键值对。这更像是一个语义问题,而不是其他任何事情。
有了这个约束,Python 中就没有什么可以做你想做的了。我建议您使用 dict 而不必担心语义上的这种详细程度。@Gabi Purcaru 的回答显示了如何使用所需的界面创建对象。为什么要为它在内部的工作方式而烦恼?
可能是 C# 的 KeyedCollection 在幕后做着同样的事情:向对象询问其密钥,然后存储该密钥以便快速访问。事实上,从文档中:
默认情况下,KeyedCollection(Of TKey, TItem) 包含一个查找字典,您可以使用 Dictionary 属性获取该字典。当一个项目被添加到 KeyedCollection(Of TKey, TItem) 时,项目的键被提取一次并保存在查找字典中以便更快地搜索。通过在创建 KeyedCollection(Of TKey, TItem) 时指定字典创建阈值来覆盖此行为。查找字典是在元素数量第一次超过该阈值时创建的。如果您指定 –1 作为阈值,则永远不会创建查找字典。
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'}
为什么不简单地使用 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
怎么样set()
?元素可以有自己的 k
为了更详细地了解@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