关于python:附加到子列表附加到每个子列表

append to a sublist appends to every sublist

我正在编写一个包含10个桶列表的简单哈希表。 索引使用内置的hash()计算,然后以表格大小为模。 但是,当我尝试将对象附加到该索引处的存储桶列表时,它会被附加到每个存储桶列表中。
我尝试过不同的方法定义add_HT,但我一直得到相同的结果。 我究竟做错了什么?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
size = 10
HT = [ [] ] * size

def add_HT(data):
    index = hash(data) % size
    HT[index].append(data)

print HT

[[], [], [], [], [], [], [], [], [], []]

add_HT('hello')

[['hello'], ['hello'], ['hello'], ['hello'], ['hello'], ['hello'], ['hello'], ['hello'], ['hello'], ['hello']]

HT = [ [] ] * size使size指向同一列表的指针数量。 add_HT不是问题所在。您需要将HT定义为[[] for i in xrange(size)]


您将HT定义为对单个列表的十个引用。相反,定义它像这样:

1
HT = [[] for _ in xrange(size)]


由于已经解释了Volatility和kindall,HT = [ [] ] * size生成相同空list的10个副本,而不是10个不同的空list s。 list身份总是咬新手程序员 - 甚至偶尔会咬住专家。

他们已经向您展示了如何解决问题并获得10个不同的空list。但是还有另一种解决这个问题的方法。如果您可以重写程序以完全不改变list,那么它们是否相同并不重要:

1
2
3
def add_HT(data):
    index = hash(data) % size
    HT[index] = HT[index] + [data]

现在:

1
2
3
4
>>> add_HT('hello')
>>> add_HT('goodbye')
>>> HT
[['goodbye'], ['hello'], [], [], [], [], [], [], [], []]

这里发生的是你每次追加时都要制作一个新桶,并更换旧桶,这样桶就是不可变的。 (您可能希望将它们存储为tuple而不是list,以确保不会意外地改变它们。)

您可以更进一步,并且不仅使每个HT[i]不可变,而且还使HT本身:

1
2
3
4
def add_HT(data):
    global HT
    index = hash(data) % size
    HT = [bucket if i != index else bucket + [data] for i, bucket in enumerate(HT)]

有时使事物不可变会使代码更简单,有时更复杂。 (在这种情况下,我认为第一个不可变版本与可变版本一样简单,但第二个版本的可读性要低得多。)有时它会使代码更快,有时更慢。 (在这种情况下,快速测试显示第一个速度大约相同,但第二个速度相差50倍,并且使用了更多内存。另一方面,使用PyPy代替CPython,它们反而是15%和30%慢,分别......)但它总是让你更容易推理 - 你不必担心对象的身份。除非它使事情更容易编写,更容易阅读和更快,否则需要考虑一个权衡。但值得知道如何做到这一点。


这是正确的版本:

1
2
3
4
5
6
7
8
9
10
11
size = 10
HT = [ [] for x in range(size)]

def add_HT(data):
    index = hash(data) % size
    HT[index].append(data)

print HT

add_HT('hello')
print HT

输出:

1
2
3
4
>>>
[[], [], [], [], [], [], [], [], [], []]
[[], ['hello'], [], [], [], [], [], [], [], []]
>>>