为什么Python会修改不在循环中的列表?

Why does Python modify the list which is out of the loop?

这份清单:

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

我运行sliding_tristep()方法,包括predict()window()方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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

当我运行sliding_tristep(current_trace)时,在print current_input, accumulator行的输出中,我注意到current_input已被修改,尽管它超出了while循环,在sliding_tristep(current_trace)中进行计算。

我想知道为什么会这样?对于python来说,如何修改在随后的循环中根本不使用的列表呢?


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

刚刚测试了您的代码:

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

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.

不过,我猜你是指current_input,而不是current_trace

current_input被修改,因为trace是对current_input的引用,跟踪被修改。

如果你想把current_input复制成trace,有一种方法可以做到:

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

应用于代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
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

如果不修改列表中的元素(由于整数是不可变的,所以不能修改),那么可以像我在前面的示例中建议的那样进行浅拷贝。如果您使用的是可变对象(lists或其他类型的对象),那么您需要使用copy模块进行深度复制。看看这个答案:https://stackoverflow.com/a/184660/1290438。


trace.extend(current_input)代替trace = current_input解决了这个问题。尽管如此,一个trace列表必须预先初始化。

解决方案如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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