Python:从集合中检索项目

时间:2011-05-12 14:48:47

标签: python python-3.x set

通常,Python集似乎不是为按键检索项而设计的。这显然是字典的用途。但无论如何,如果给出一个键,你可以从一个等于键的集合中检索一个实例吗?

同样,我知道这正是字典的用途,但据我所知,有一些合理的理由想要用字符集来实现。假设你有一个类定义如下:

class Person:
   def __init__(self, firstname, lastname, age):
      self.firstname = firstname
      self.lastname = lastname
      self.age = age

现在,假设我要创建大量Person个对象,每次创建一个Person对象时,我需要确保它不是以前的Person副本。 1}}对象。如果Person具有相同的Person,则firstname被视为另一个Person的副本,无论其他实例变量如何。很自然地,显而易见的事情是将所有__hash__个对象插入到集合中,并定义__eq__Person方法,以便firstname个对象按其{{1}进行比较}}

备用选项是创建Person个对象的字典,并使用单独创建的firstname字符串作为键。这里的缺点是我要复制firstname字符串。在大多数情况下,这不是一个真正的问题,但如果我有10,000,000个Person个对象怎么办?冗余字符串存储可能真的开始在内存使用方面加起来。

但如果两个Person个对象的比较相同,我需要能够检索原始对象,以便可以按照业务需要的方式合并其他实例变量(firstname除外)逻辑。这让我回到了我的问题:我需要一些方法来从set检索实例。

有没有这样做?或者在这里使用字典是唯一真正的选择?

3 个答案:

答案 0 :(得分:8)

我肯定会在这里使用字典。将firstname实例变量重用为字典键不会复制它 - 字典将只使用相同的对象。我怀疑字典会使用比一组更多的内存。

要实际节省内存,请在类中添加__slots__属性。这将阻止每个10,000,000个实例拥有__dict__属性,这将比dict set上的def rand_str(): return str.join("", (chr(random.randrange(97, 123)) for i in range(random.randrange(3, 16)))) class A(object): def __init__(self): self.x = rand_str() self.y = rand_str() def __hash__(self): return hash(self.x) def __eq__(self, other): return self.x == other.x 的潜在开销节省更多内存。

修改:一些数字可以支持我的说法。我定义了一个存储随机字符串对的愚蠢示例类:

random.seed(42)
s = set(A() for i in xrange(1000000))

此类的一组1,000,000个实例使用的内存量

    __slots__ = ("x", "y")

在我的机器上240 MB。如果我添加

def key_value():
    a = A()
    return a.x, a

random.seed(42)
d = dict(key_value() for i in xrange(1000000))

到这个课程,这个数字下降到112 MB。如果我将相同的数据存储在字典中

__slots__

这使用了249 MB没有__slots__和121 MB带{{1}}。

答案 1 :(得分:3)

是的,你可以这样做:set可以迭代。但请注意,这是一个 O(n)操作,而不是dict的 O(1)操作。

因此,您必须以速度内存进行权衡。这是经典之作。我个人会在这里进行优化(即使用字典),因为只有10,000,000个对象,内存不会那么快,并且使用字典非常容易。

至于firstname字符串的额外内存消耗:由于字符串在Python中是不可变的,因此将firstname属性指定为键不会创建新字符串,而只是复制引用。

答案 2 :(得分:1)

我想你会在这里得到答案:

Moving Beyond Factories in Python