Python:从集中检索项目


问题内容

通常,Python集似乎并不是为通过键检索项目而设计的。显然,这就是字典的作用。但是无论如何,给定一个键,您是否可以从一个等于该键的集合中检索一个实例?

同样,我知道这正是字典的用途,但据我所知,有合理的理由要使用一组字典。假设您有一个定义如下的类:

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

现在,假设我将要创建大量Person对象,并且每次创建Person对象时,我都需要确保它不是先前Person对象的副本。如果A具有相同的值,Person则认为A是另一个的重复项,而与其他实例变量无关。因此,自然而然的事情就是将所有对象插入到一个集合中,并定义一个and方法,以便将它们的对象进行比较。Person``firstname``Person``__hash__``__eq__``Person``firstname

另一种选择是创建Person对象字典,并使用单独创建的firstname字符串作为键。这里的缺点是我将复制firstname字符串。在大多数情况下,这并不是真正的问题,但是如果我有10,000,000个Person对象怎么办?冗余字符串存储实际上可以开始增加内存使用量。

但是,如果两个Person对象比较相等,则我需要能够检索原始对象,以便firstname可以按业务逻辑所需的方式合并其他实例变量(除之外)。这使我回到问题所在:我需要某种方法来从中检索实例set

反正有这样做吗?还是在这里使用字典是唯一的选择?


问题答案:

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

要实际节省内存,__slots__请在类中添加一个属性。这将防止每次10,000,000实例从具有__dict__属性,这将节省更多的内存比的潜在开销dictset

编辑 :一些数字来支持我的主张。我定义了一个愚蠢的示例类,存储了成对的随机字符串:

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

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

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

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

    __slots__ = ("x", "y")

到该类,这下降到112 MB。如果我在字典中存储相同的数据

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

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

如果不使用则使用249 MB __slots__,使用则使用121 MB __slots__