Python: два объекта, похоже, ссылаются на одну и ту же переменную, хотя у меня нет явной связи между ними - PullRequest
0 голосов
/ 17 сентября 2018

Я пытаюсь изменить октет, основываясь на маске, которая у меня есть. Допустим, у меня есть IP: 194.216.35.54 и маска-27, мне нужно изменить 4-й октет (3, если считать от 0). Я пытаюсь изменить значения в переменной work_octets. Но, так или иначе, значения в network_octets также меняются. Почему это происходит? network_octets и work_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 и vals должны оставаться такими же. Но все переменные модифицируются после цикла for

1 Ответ

0 голосов
/ 17 сентября 2018

Существует много кода, и ваш фрагмент не является исполняемым, как есть, поэтому немного сложно дать однозначные ответы. Тем не менее, я думаю, что проблема заключается в том, как вы создали свои списки.

Если я правильно понял ваш код, network_octets и work_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)

Будьте осторожны в своем коде, так как комментарии предполагают, что ваши списки на самом деле содержат словари словарей. Следовательно, вышеупомянутая проблема также применима, если вы копируете только внешние. Глубокая копия, безусловно, будет более безопасной, но и более дорогой, если это может быть проблемой.

Вы можете попробовать print-debug с помощью is (как я это делал в моих примерах) или id, чтобы убедиться, что словари отличаются, прежде чем их изменять:

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