我需要一组对象,这些对象可以通过每个对象共有的某个(唯一)属性进行查找。现在我正在使用一个dicitionary将字典键分配给属性。 以下是我现在所拥有的一个例子:
class Item():
def __init__(self, uniq_key, title=None):
self.key = uniq_key
self.title = title
item_instance_1 = Item("unique_key1", title="foo")
item_instance_2 = Item("unique_key3", title="foo")
item_instance_3 = Item("unique_key2", title="foo")
item_collection = {
item_instance_1.key: item_instance_1,
item_instance_2.key: item_instance_2,
item_instance_3.key: item_instance_3
}
item_instance_1.key = "new_key"
现在这似乎是一个相当麻烦的解决方案,因为密钥不是对属性的引用,而是在赋值时获取key-attribute的值,这意味着:
使用列表并迭代对象似乎效率更低。
那么,对于这种特殊情况,是否有比dict更合适的数据结构,一组对象根据某个对象属性给我随机访问?
这需要与Python 2.4一起使用,因为这就是我所困扰的(在工作中)。
如果不是很明显,我是Python的新手。
答案 0 :(得分:5)
实际上,您不必担心重复信息:dict的密钥和对象的.key
属性只是对完全相同对象的两个引用。
唯一真正的问题是“如果重新分配.key
会怎么样”。那么,显然你必须使用一个属性来更新所有相关的dicts以及实例的属性;所以每个对象必须知道它可以被注册的所有序列。理想情况下,人们可能希望为此目的使用弱引用,以避免循环依赖,但是,唉,你不能将weakref.ref
(或代理)带到dict。所以,我在这里使用普通引用(替代方法不是使用dict
实例,而是使用一些特殊的子类 - 不方便)。
def enregister(d, obj):
obj.ds.append(d)
d[obj.key] = obj
class Item(object):
def __init__(self, uniq_key, title=None):
self._key = uniq_key
self.title = title
self.ds = []
def adjust_key(self, newkey):
newds = [d for d in self.ds if self._key in d]
for d in newds:
del d[self._key]
d[newkey] = self
self.ds = newds
self._key = newkey
def get_key(self):
return self._key
key = property(get_key, adjust_key)
编辑:如果您想要一个包含Item的所有实例的单个集合,那就更容易了,因为您可以使集合成为类级属性;事实上它可以是一个WeakValueDictionary,以避免错误地保持项目存活,如果这是你需要的。即:
class Item(object):
all = weakref.WeakValueDictionary()
def __init__(self, uniq_key, title=None):
self._key = uniq_key
self.title = title
# here, if needed, you could check that the key
# is not ALREADY present in self.all
self.all[self._key] = self
def adjust_key(self, newkey):
# "key non-uniqueness" could be checked here too
del self.all[self._key]
self.all[newkey] = self
self._key = newkey
def get_key(self):
return self._key
key = property(get_key, adjust_key)
现在,您可以使用Item.all['akey']
,Item.all.get('akey')
,for akey in Item.all:
等等 - 所有丰富的dicts功能。
答案 1 :(得分:2)
你可以在这里做很多好事。一个例子是让班级跟踪所有事情:
class Item():
_member_dict = {}
@classmethod
def get_by_key(cls,key):
return cls._member_dict[key]
def __init__(self, uniq_key, title=None):
self.key = uniq_key
self.__class__._member_dict[key] = self
self.title = title
>>> i = Item('foo')
>>> i == Item.get_by_key('foo')
True
请注意,您将保留更新问题:如果key
发生更改,则_member_dict
会失去同步。这就是封装将派上用场的地方:在没有更新字典的情况下,让它(实际上)无法更改key
。有关如何执行此操作的详细教程,请参阅this tutorial。
答案 2 :(得分:0)
嗯,dict真的是你想要的。可能麻烦的不是字典本身,而是你建立它的方式。这是对您的示例的一个小改进,展示了如何使用列表表达式和dict构造函数来轻松创建查找字典。这还显示了如何创建多图表类型的dict,以查找给定字段值的匹配项目,这些字段值可能跨项目重复:
class Item(object):
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
def __str__(self):
return str(self.__dict__)
def __repr__(self):
return str(self)
allitems = [
Item(key="red", title="foo"),
Item(key="green", title="foo"),
Item(key="blue", title="foofoo"),
]
# if fields are unique
itemByKey = dict([(i.key,i) for i in allitems])
# if field value can be duplicated across items
# (for Python 2.5 and higher, you could use a defaultdict from
# the collections module)
itemsByTitle = {}
for i in allitems:
if i.title in itemsByTitle:
itemsByTitle[i.title].append(i)
else:
itemsByTitle[i.title] = [i]
print itemByKey["red"]
print itemsByTitle["foo"]
打印:
{'key': 'red', 'title': 'foo'}
[{'key': 'red', 'title': 'foo'}, {'key': 'green', 'title': 'foo'}]
答案 3 :(得分:0)
编辑以纠正我遇到的问题 - 这是由于我的“collection = dict()”默认参数(*bonk*).
现在,每次调用该函数都会返回一个具有自己的集合的类 - 这是如果需要多个这样的收集,便利。我也把这个集合放在类中,只是像以前一样在一个元组中单独返回类而不是两个。 (将默认容器保留为dict(),但可以更改为Alex的WeakValueDictionary,这当然非常酷。)
def make_item_collection(container = None):
''' Create a class designed to be collected in a specific collection. '''
container = dict() if container is None else container
class CollectedItem(object):
collection = container
def __init__(self, key, title=None):
self.key = key
CollectedItem.collection[key] = self
self.title = title
def update_key(self, new_key):
CollectedItem.collection[
new_key] = CollectedItem.collection.pop(self.key)
self.key = new_key
return CollectedItem
# Usage Demo...
Item = make_item_collection()
my_collection = Item.collection
item_instance_1 = Item("unique_key1", title="foo1")
item_instance_2 = Item("unique_key2", title="foo2")
item_instance_3 = Item("unique_key3", title="foo3")
for k,v in my_collection.iteritems():
print k, v.title
item_instance_1.update_key("new_unique_key")
print '****'
for k,v in my_collection.iteritems():
print k, v.title
这是Python 2.5.2中的输出:
unique_key1 foo1
unique_key2 foo2
unique_key3 foo3
****
new_unique_key foo1
unique_key2 foo2
unique_key3 foo3