copying 2D arrays in python

Question:

I’m trying to copy a 2D array to another 2D array in python, then change the copy, without changing any other 2D arrays which are a copy of the original 2D array and they’re acting rather strangely. Upon consulting stackoverflow when I first needed to copy 2D arrays into other 2D arrays, I was prompted to use the copy() and deepcopy() operations from the copy library. However, this code is acting rather oddly and I can’t seem to figure out why.

This is the code:

import copy

r1 = [4,10,12,5,11,6,3,16,21,25,13,19,14,22,24,7,23,20,18,15,0,8,1,17,2,9],[20,22,24,6,0,3,5,15,21,25,1,4,2,10,12,19,7,23,18,11,17,8,13,16,14,9],[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25],[24],[0]
r2 = [0,9,3,10,18,8,17,20,23,1,11,7,22,19,12,2,16,6,25,13,15,24,5,21,14,4],[0,9,15,2,25,22,17,11,5,1,3,10,14,19,24,20,16,6,4,13,7,23,12,8,21,18],[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25],[12],[0]
r3 = [1,3,5,7,9,11,2,15,17,19,23,21,25,13,24,4,8,22,6,0,10,12,20,18,16,14],[19,0,6,1,15,2,18,3,16,4,20,5,21,13,25,7,24,8,23,9,22,11,17,10,14,12],[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25],[3],[0]
r4 = [4,18,14,21,15,25,9,0,24,16,20,8,17,7,23,11,13,5,19,6,10,3,2,12,22,1],[7,25,22,21,0,17,19,13,11,6,20,15,23,16,2,4,9,12,1,18,10,3,24,14,8,5],[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25],[17],[0]
r5 = [21,25,1,17,6,8,19,24,20,15,18,3,13,7,11,23,0,22,12,9,16,14,
5,4,2,10],[16,2,24,11,23,22,4,13,5,19,25,14,18,12,21,9,20,3,10,6,8,0,17,15,7,1],[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25],[7],[0]
#empty slots for the rotors
slot1 = [],[],[],[],[]
slot2 = [],[],[],[],[]
slot3 = [],[],[],[],[]

class rotors:
    def set_rotors(self,s1,s2,s3):
        global slot1
        global slot2
        global slot3

        slots=[s1,s2,s3]
        rotors=[r1,r2,r3,r4,r5]
        for sn in slots:
            slotn= copy.copy(rotors[sn-1])
            if sn == s1:
                slot1 = slotn
                self.s1=sn
            if sn == s2:
                slot2=slotn
                self.s2=sn
            if sn == s3:
                slot3=slotn
                self.s3=sn
                
    def value(self, rotor):
        if rotor == 1:
            #return(alphabet[(slot1[2][signal]-slot1[4][0])%26])
            return(slot1[0])
        elif rotor == 2:
            #return(alphabet[(slot2[2][signal]-slot2[4][0])%26])
            return(slot2[0])
        elif rotor == 3:
            #return(alphabet[(slot3[2][signal]-slot3[4][0])%26])
            return(slot3[0])
        
    def ring_setting_down(self,slot,setting):
        if slot==1:
            for i in range(26):
                slot1[0][i] = (slot1[0][i] - setting)%26
            for i in range(setting):
                slot1[1].insert(25,slot1[1].pop(0))
            print("test 1")
        elif slot==2:
            for i in range(26):
                slot2[0][i] = (slot2[0][i] - setting)%26
            for i in range(setting):
                slot2[1].insert(25,slot2[1].pop(0))
            print("test 2")
        elif slot==3:
            for i in range(26):
                slot3[0][i] = (slot3[0][i] - setting)%26
            for i in range(setting):
                slot3[1].insert(25,slot3[1].pop(0))
            print("test 3")
        
RT=rotors()
RT.set_rotors(1,1,1)
print(RT.value(1))
print(RT.value(2))
print(RT.value(3))
RT.ring_setting_down(1,1)
print(RT.value(1))
print(RT.value(2))
print(RT.value(3))

This program has slot1, slot2, and slot3 copy the r1 2D array, and then changes slot1 with the ring_setting_down function of the class rotors.
Upon running this, I expected to get the slot1, slot2 and slot3 2D arrays and output the first array in those three, before changing slot1, and then once again outputting the first array within these three 2D arrays.
The expected output would be:

[4, 10, 12, 5, 11, 6, 3, 16, 21, 25, 13, 19, 14, 22, 24, 7, 23, 20, 18, 15, 0, 8, 1, 17, 2, 9]
[4, 10, 12, 5, 11, 6, 3, 16, 21, 25, 13, 19, 14, 22, 24, 7, 23, 20, 18, 15, 0, 8, 1, 17, 2, 9]
[4, 10, 12, 5, 11, 6, 3, 16, 21, 25, 13, 19, 14, 22, 24, 7, 23, 20, 18, 15, 0, 8, 1, 17, 2, 9]
test 1
[3, 9, 11, 4, 10, 5, 2, 15, 20, 24, 12, 18, 13, 21, 23, 6, 22, 19, 17, 14, 25, 7, 0, 16, 1, 8]
[4, 10, 12, 5, 11, 6, 3, 16, 21, 25, 13, 19, 14, 22, 24, 7, 23, 20, 18, 15, 0, 8, 1, 17, 2, 9]
[4, 10, 12, 5, 11, 6, 3, 16, 21, 25, 13, 19, 14, 22, 24, 7, 23, 20, 18, 15, 0, 8, 1, 17, 2, 9]

however, for some reason, the actual output is:

[4, 10, 12, 5, 11, 6, 3, 16, 21, 25, 13, 19, 14, 22, 24, 7, 23, 20, 18, 15, 0, 8, 1, 17, 2, 9]
[4, 10, 12, 5, 11, 6, 3, 16, 21, 25, 13, 19, 14, 22, 24, 7, 23, 20, 18, 15, 0, 8, 1, 17, 2, 9]
[4, 10, 12, 5, 11, 6, 3, 16, 21, 25, 13, 19, 14, 22, 24, 7, 23, 20, 18, 15, 0, 8, 1, 17, 2, 9]
test 1
[3, 9, 11, 4, 10, 5, 2, 15, 20, 24, 12, 18, 13, 21, 23, 6, 22, 19, 17, 14, 25, 7, 0, 16, 1, 8]
[3, 9, 11, 4, 10, 5, 2, 15, 20, 24, 12, 18, 13, 21, 23, 6, 22, 19, 17, 14, 25, 7, 0, 16, 1, 8]
[3, 9, 11, 4, 10, 5, 2, 15, 20, 24, 12, 18, 13, 21, 23, 6, 22, 19, 17, 14, 25, 7, 0, 16, 1, 8]

This is rather confusing because the testing prints (very professional, I know) imply the only part of the if statements ring_setting_down() is the one that changes slot1, yet slot2 and slot3 have somehow also been changed in the process. Swapping the copy.copy() statement in the set_rotors() function for a copy.deepcopy() statement seems to give the same result.

Can anyone else figure out what is going on here and how I could modify this code to give me the expected output?

Asked By: alanhasmemes

||

Answers:

Try to produce a minimal example to make it easier to answer in the future.

However, it seems like you should use copy.deepcopy() rather than copy.copy() to fix your issue ([copy.deepcopy(rotors[sn-1]) for sn in slots])

Answered By: wooluk

You seem to have two issues: 1) copy.deepcopy should be used. 2) The problem appears to be in the next few lines:

            if sn == s1:
                slot1 = slotn
                self.s1=sn
            if sn == s2:
                slot2=slotn
                self.s2=sn
            if sn == s3:
                slot3=slotn
                self.s3=sn

This code will make slot1, slot2, and slot3 all aliases of each other because you call set_rotors() with 1, 1, and 1. The value of sn is equal to s1, s2, and s3 every time, so it sets slot1, slot2, and slot3 to the same list of slotn all 3 times the loop runs.

As for a solution: Getting rid of the loop doesn’t seem to be too much of an issue. You could just do this:

slot1 = copy.deepcopy(rotors[s1-1])
self.s1=s1
slot2 = copy.deepcopy(rotors[s2-1])
self.s2=s2
slot3 = copy.deepcopy(rotors[s3-1])
self.s3=s3

If keeping the loop is important because you have more values,

Answered By: Gannon