Python:即使我在两个对象之间没有任何明确的联系,两个对象似乎在引用同一变量

时间:2018-09-17 07:54:13

标签: python list object-reference object-slicing

我正在尝试根据我拥有的面罩更改一个八位位组。 可以说我有IP:194.216.35.54和mask- 27,我需要更改第4个八位位组(从0开始计数为3)。我正在尝试更改work_octets变量中的值。但是,以某种方式,network_octets中的值也在改变。为什么会这样呢? network_octetswork_octets都是两个不同的列表,它们是通过对值进行循环并切片ip_dict_list来创建的。 network_octets的变化如何-有人可以帮助我了解我在这里做错了什么吗?

注意:我可以进行深度复制和更改此行为,但是我需要知道我在这里做错了什么。我们对此表示感谢。

代码:

octet = 3
ip_dict_list = dc(self.ip_dict_list) # looks like this: [{1: {128: 0, 64: 0, 2: 0, 4: 0, 1: 0, 16: 0, 32: 0, 8: 0}}, {2: {128: 0, 64: 0, 2: 0, 4: 0, 1: 0, 16: 0, 32: 0, 8: 0}}, {3: {128: 0, 64: 0, 2: 0, 4: 0, 1: 0, 16: 0, 32: 0, 8: 0}}, {4: {128: 0, 64: 0, 2: 0, 4: 0, 1: 0, 16: 0, 32: 0, 8: 0}}]

vals = [v for i in ip_dict_list for k, v in i.items()]
network_octets = vals[:octet]
work_octets = vals[octet:]
ip_list = ip.split('.')
iof = int(ip_list[octet])  # ip octet in focus (54)
for i in range(len(work_octets)):
    for ob in self.bit_placement:  # loop over the list [128, 64, 32, 16, 8, 4, 2, 1]
        # 32 < 54
        if ob <= iof:
            print "Iof: {}, Ob: {}".format(iof, ob)
            iof = iof - ob  # 54-32 = 22; 22-16 = 6; 6-4: 2; 2-2 = 0 (done)
            work_octets[i][ob] = 1  # {32: 1, 16: 0, 8: 0, 4: 0, 2: 0, 1:0 }
            if iof == 0:
                break

Iof: 54, Ob: 32
Iof: 22, Ob: 16
Iof: 6, Ob: 4
Iof: 2, Ob: 2
print work_octets # as expected
[{128: 0, 64: 0, 2: 1, 4: 1, 1: 0, 8: 0, 16: 1, 32: 1}]
  

现在network_octets也已更改,我希望它保持不变-不符合预期

print network_octets 
[{128: 0, 64: 0, 2: 1, 4: 1, 1: 0, 8: 0, 16: 1, 32: 1}, {128: 0, 64: 0, 2: 1, 4: 1, 1: 0, 8: 0, 16: 1, 32: 1}, {128: 0, 64: 0, 2: 1, 4: 1, 1: 0, 8: 0, 16: 1, 32: 1}]
  

这应该保持不变,并且应该像这样:

network_octets
[{128: 0, 64: 0, 2: 0, 4: 0, 1: 0, 8: 0, 16: 0, 32: 0}, {128: 0, 64: 0, 2: 0, 4: 0, 1: 0, 8: 0, 16: 0, 32: 0}, {128: 0, 64: 0, 2: 0, 4: 0, 1: 0, 8: 0, 16: 0, 32: 0}]

The variable vals is also changing after the for loop:
vals
[{128: 0, 64: 0, 2: 1, 4: 1, 1: 0, 8: 0, 16: 1, 32: 1}, {128: 0, 64: 0, 2: 1, 4: 1, 1: 0, 8: 0, 16: 1, 32: 1}, {128: 0, 64: 0, 2: 1, 4: 1, 1: 0, 8: 0, 16: 1, 32: 1}, {128: 0, 64: 0, 2: 1, 4: 1, 1: 0, 8: 0, 16: 1, 32: 1}]

我希望只有work_octets的dict元素的值发生变化,并且network_octets和val应当保持不变。但是所有的变量在for循环之后都会被修改

1 个答案:

答案 0 :(得分:1)

有很多代码,您的代码段无法按原样执行, 因此给出确切的答案有点困难。 不过,我认为问题在于您如何创建列表。

如果我正确理解了您的代码, network_octetswork_octets是两个字典列表。 但是那些是相同的字典,所以即使您有不同的列表 (即network_octets is work_octets将是False), 它们的内容是相同的 (即network_octets[0] is work_octets[0]将是True)。 因此,当您分配work_octets[i][ob]时, 您还可以触摸network_octets[i]中的字典。 您可以通过打印ip_dict_list来确认, 应该也应该改变了。

我们可以用一个最小的例子重现该问题:

source = [{0: 10, 1: 20}, {0: 30, 1: 40}, {0: 50, 1: 60}]
l1 = source[:2]
l2 = source[:2]

# Prints 'True'
print(l1[0] is l2[0])

# Here we're effectively touching `l1[0][0]` *and* `l2[0][0]`
l1[0][0] = 100

# Prints '[{0: 100, 1: 20}, {0: 30, 1: 40}]'
print(l2)

您不必完全复制所有内容。 您只能专注于要修改的对象。 例如,在我的示例中,它可以归结为以下内容:

# ...
l1[0] = dict(l1[0])
l1[0][0] = 100

# Prints '[{0: 100, 1: 20}, {0: 30, 1: 40}]'
print(l1)

# Prints '[{0: 10, 1: 20}, {0: 30, 1: 40}]'
print(l2)

在代码中要小心,因为注释表明您的列表实际上包含字典。 因此,如果仅浅拷贝外部的问题,上述问题也将适用。 深拷贝肯定会更安全,但如果有任何担心的话,也会更昂贵。

您可以尝试使用is(就像我在示例中所做的那样)或id print-debug 来确保字典在更改之前是不同的:

# Prints '4406947912 4406947912'
# Obviously the numbers could be different every time you run your code.
print(id(l1[0]), id(l2[0]))