1

I have a small function which uses one list to populate another. For some reason, the source list gets modified. I don't have a single line that manipulates the source list arr. I am probably missing the way Python deals with scope of variables, lists. My expected output is for the list arr to remain the same after the function call.

numTestRows = 5
m = 2
def getTestData():
    data['test'] = []
    size_c = len(arr)
    for i in range(numTestRows):
        data['test'].append(arr[i%size_c])
        for j in range(m):
            data['test'][i].append('xyz')

#just a 2x5 str matrix
arr = [['a', 'b', 'c', 'd', 'e'], ['f', 'g', 'h', 'i', 'j']]
print('Array before: ')
print( arr)
data = {}
getTestData()
print('Array after: ')
print( arr)

Output

Array before: 
[['a', 'b', 'c', 'd', 'e'], ['f', 'g', 'h', 'i', 'j']]
Array after: 
[['a', 'b', 'c', 'd', 'e', 'xyz', 'xyz', 'xyz', 'xyz', 'xyz', 'xyz'], ['f', 'g', 'h', 'i', 'j', 'xyz', 'xyz', 'xyz', 'xyz']]
8
  • 1
    It's better if you passed your variables (data and arr) as arguments to getTestData() Commented Sep 10, 2018 at 18:00
  • Why? I know it's considered a good practice but what about cases when I need the variable across functions, global is better than passing to each one. Commented Sep 10, 2018 at 18:02
  • 1
    Why are global variables bad? Commented Sep 10, 2018 at 18:03
  • It appear to me that data['test'].append(arr[i%size_c]) is appending a reference to the element of the array and data['test'][i].append('xyz') data to this object it has stored. Commented Sep 10, 2018 at 18:05
  • 1
    To fix the issue change the line data['test'].append(arr[i%size_c]) to data['test'].append(arr[i%size_c][:]) to make a new copy of the array when you are appending it to a dictionary rather than adding the reference to it. stackoverflow.com/questions/8744113/… is about copying by value rather than by reference. Commented Sep 10, 2018 at 18:14

1 Answer 1

2

You've mis-handled the references in your list of lists (not a matrix). Perhaps if we break this down a little more, you can see what's happening. Start your main program with the two char lists as separate variables:

left  = ['a', 'b', 'c', 'd', 'e']
right = ['f', 'g', 'h', 'i', 'j']
arr = [left, right]

Now, look at what happens within your function at the critical lines. On this first iteration, size_c is 2, i is 0 ...

    data['test'].append(arr[i%size_c])

This will append arr[0] to data[test], which started as an empty list. Now for the critical part: arr[0] is not a new list; rather, it's a reference to the list we now know as left in the main program. There is only one copy of this list.

Now, when we get into the next loop, we hit the statement:

        data['test'][i].append('xyz')

data['test'][i] is a reference to the same list as left ... and this explains the appending to the original list.

You can easily copy a list with the suffix [:], making a new slice of the entire list. For instance:

data['test'].append(arr[i%size_c][:])

... and this should solve your reference problem.

Sign up to request clarification or add additional context in comments.

1 Comment

You can also use copy(), which works more generally than just lists.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.