2

for this list:

current_trace = [[3,5,1,5,7,9,4]]

I run the sliding_tristep() method, which includes the predict() and window() methods:

def predict(lst):
    print "predicting for", lst
    print "result", max(lst) + 0.0
    return max(lst) + 0.0

def window(lst, n=3):
    for x in range(1, len(lst)+1): # or len(l)+n to continue till the end
        yield(lst[max(0, x-n):x])

def sliding_tristep(full_trace, future_step = 2, window_size = 3):

    for user_trace in full_trace:
        for current_input in window(user_trace):
            counter = 0
            trace = current_input
            accumulator = []
            while counter <= future_step:
                next_prediction = predict(trace)  
                trace.append(next_prediction)
                accumulator.append(next_prediction)
                trace = trace[-window_size:]
                counter += 1

            print current_input, accumulator

When I run sliding_tristep(current_trace), in the output for the print current_input, accumulator line I notice that the current_input has been modified although it is out of the while loop which makes the calculations in sliding_tristep(current_trace).

I wonder why does this happen? How is that possible for python to modify a list which is not used at all in the subsequent loop.

zmo
  • 24,463
  • 4
  • 54
  • 90
Kristof Pal
  • 966
  • 4
  • 12
  • 28

2 Answers2

2

I run sliding_tristep(current_trace), in the output for the print current_input, accumulator line I notice that the current_trace has been modified

just tested your code:

>>> current_trace = [[3,5,1,5,7,9,4]]
>>> sliding_tristep(current_trace)
...
>>> current_trace
[[3, 5, 1, 5, 7, 9, 4]]

current_trace does not get modified.

I wonder why does this happen? How is that possible for python to modify a list which is not used at all in the subsequent loop.

Though, I guess you meant current_input and not current_trace.

current_input gets modified, because trace is a reference to current_input and trace gets modified.

If you want to make a copy of current_input as trace, here's one way to do it:

>>> foo = [1,2,3]
>>> bar = foo[:]
>>> bar.append(4)
>>> foo
[1, 2, 3]
>>> bar
[1, 2, 3, 4]

applied to your code:

def sliding_tristep(full_trace, future_step = 2, window_size = 3):
    for user_trace in full_trace:
        for current_input in window(user_trace):
            counter = 0
            trace = current_input[:] # make a copy of current_input
            accumulator = []
            while counter <= future_step:
                next_prediction = predict(trace)  
                trace.append(next_prediction)
                accumulator.append(next_prediction)
                trace = trace[-window_size:]
                counter += 1                    
            print current_input, accumulator

If you don't modify the elements of the list (and as integers are non-mutable, you can't), you can do the shallow copy as I'm suggesting you in the aforementioned example. If you're using mutable objects (lists or other kind of objects), then you want to do a deep copy using the copy module. Look at this answer: https://stackoverflow.com/a/184660/1290438 on this topic.

Community
  • 1
  • 1
zmo
  • 24,463
  • 4
  • 54
  • 90
  • 1
    Parameter passing is independent of mutability. Passing an immutable object also gives the function a "reference". –  Feb 13 '14 at 11:25
  • it's independant, but it's two conditions that are enough for making a mistake. Anyway, I had a better and more thorough look into his code, and tried to understand what he *meant* behind what he *says*. – zmo Feb 13 '14 at 11:39
  • yes you are right, I meant `current_input` instead of `current_trace`. – Kristof Pal Feb 13 '14 at 12:46
0

using trace.extend(current_input) , instead of trace = current_input solves the problem. although, a trace list has to be initialized beforehand.

The solution would look like:

def sliding_tristep(full_trace, future_step = 2, window_size = 3):

    for user_trace in full_trace:
        for current_input in window(user_trace): 
            counter = 0
            trace = [] #here is the solution
            trace.extend(current_input) #here is the solution
            accumulator = []
            while counter <= future_step:
                next_prediction = predict(trace)  
                trace.append(next_prediction)
                accumulator.append(next_prediction)
                trace = trace[-window_size:]
                counter += 1
Kristof Pal
  • 966
  • 4
  • 12
  • 28
  • well, you don't need to do an empty list and copy the content of the other list in two steps, you can do it in one step using `trace = current_input[:]` as I tell you in my answer. Also, do not comment on your question using an answer, but update your question or make a comment! – zmo Feb 13 '14 at 13:16
  • @zmo: the person who asked a question can most definitely leave an answer to indicate how he solved the problem; provided the specific solution isn't already mentioned. You cannot update your question with the solution, that would defeat the entire purpose of having a separate answer section. – Jeroen Vannevel Feb 14 '14 at 19:11
  • well, true for not updating the question, but what he says can definitely fit in a comment. And btw, the solution has already been given in my answer, and the cause of his problem exposed in a comment. Basically, the OP answering himself is just non-sense. – zmo Feb 15 '14 at 11:33