一般来说,Python 集似乎并不是为按键检索项目而设计的。这显然是字典的用途。但是无论如何,给定一个键,您是否可以从一个等于键的集合中检索一个实例?
再一次,我知道这正是字典的用途,但据我所知,有正当理由想要对集合执行此操作。假设您有一个定义如下的类:
class Person:
def __init__(self, firstname, lastname, age):
self.firstname = firstname
self.lastname = lastname
self.age = age
现在,假设我要创建大量的 Person
对象,每次我创建一个 Person
对象时,我都需要确保它不是先前 Person
对象的副本。一个 Person
被认为是另一个 Person
的副本,如果他们有相同的 firstname
,而不考虑其他实例变量。所以很自然地,要做的事情就是将所有 Person
对象插入到一个集合中,并定义一个 __hash__
和 __eq__
方法,以便 Person
对象按它们的 firstname
进行比较。
另一种选择是创建 Person
对象的字典,并使用单独创建的 firstname
字符串作为键。这里的缺点是我会复制 firstname
字符串。在大多数情况下这并不是真正的问题,但是如果我有 10,000,000 个 Person
对象怎么办?就内存使用而言,冗余字符串存储可能真的开始增加。
但是如果两个 Person
对象比较相等,我需要能够检索原始对象,以便可以将其他实例变量(firstname
除外)合并到业务逻辑所需的一种方式。这让我回到了我的问题:我需要一些方法来从 set
中检索实例。
有没有办法做到这一点?还是使用字典是这里唯一真正的选择?
最佳答案
我肯定会在这里使用字典。将 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
该类的一组 1,000,000 个实例使用的内存量
random.seed(42)
s = set(A() for i in xrange(1000000))
在我的机器上是 240 MB。如果我添加
__slots__ = ("x", "y")
对于类(class)来说,这下降到 112 MB。如果我将相同的数据存储在字典中
def key_value():
a = A()
return a.x, a
random.seed(42)
d = dict(key_value() for i in xrange(1000000))
这在没有 __slots__
的情况下使用 249 MB,在有 __slots__
的情况下使用 121 MB。
关于Python:从集合中检索项目,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5979861/