파이썬에서 list를 사용시에 가장 오른쪽에 있는 요소를 제거하는 방법은 여러 가지가 있다.

  1. list = list[:-1] : 슬라이스를 사용해서 끝의 원소를 제거한 리스트를 반환받는 것이다.
  2. list.pop() : 가장 끝에 있는 원소를 pop하는 것이다.

실험

50000개의 요소를 가진 리스트를 리스트가 비워질 때까지 계속 끝의 원소를 제거하고 그 시간을 측정한다.

실험 전 생각

아마 pop을 사용하는 방법이 가장 빠를 것으로 예상된다. 1번처럼 슬라이스를 사용하여 반환받을 경우 리스트를 새로 대입해야하기 때문이다.

1
2
import time
LIST_RANGE = 50000
1
2
3
4
5
6
7
8
9
# list.pop()
test = [i for i in range(LIST_RANGE)]
start = time.time()
while test:
    test.pop()
end = time.time()
print(f'result: {end-start}')

>>> result: 0.011001348495483398
1
2
3
4
5
6
7
8
9
# list = list[:-1]
test = [i for i in range(LIST_RANGE)]
start = time.time()
while test:
    test = test[:-1]
end = time.time()
print(f'result: {end-start}')

>>> result: result: 5.4392759799957275

여러 번 계산 후에 평균을 내려고 했으나 너무 오래걸려서 그냥 한번만 돌렸다. 그래도 결과는 너무 자명했다. 끝의 원소를 inplace로 제거하고싶을 경우 절대로 슬라이스를 쓰지말고 pop()를 쓰자.

번외 실험

deque가 훨씬 빠른 것은 파이썬의 큐 모듈 속도 비교에서 해 보았다시피 deque가 훨씬 빨랐다. 그렇다면 listdeque에 넣고 모두 pop하는 것과 listpop하는 것중 무엇이 더 빠를까?

실험

$2^k$ 개의 원소를 집어넣은 list와 해당 listdeque에 넣고 모두 빼는 연산의 속도를 비교한다. $k \in {8,9, 10, 11,12 }$

각 실험은 10번의 평균값을 낸다.

실험 전 생각

list.pop이 빠를 것 같지만 pop만 보면 deque가 더 빠르기 때문에 요소 수에 따라 결과가 달라질 수도 있다는 생각을 했다.

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import time
from collections import deque
import numpy as np

k_list = [1 << i for i in range(8, 13)]
K_LENGTH = len(k_list)
ITER = 10

only_list_time = np.zeros((K_LENGTH, ITER))
for k_idx, k in enumerate(k_list):
    for i in range(ITER):
        test = [i for i in range(k)]
        start = time.time()
        while test:
            test.pop()
        end = time.time()
        only_list_time[k_idx][i] = start-end

result_only_list = only_list_time.mean(axis=1)

list_deque_time = np.zeros((K_LENGTH, ITER))
for k_idx, k in enumerate(k_list):
    for i in range(ITER):
        test = [i for i in range(k)]
        start = time.time()
        while test:
            test = deque(test)
            test.pop()
        end = time.time()
        list_deque_time[k_idx][i] = start-end

result_list_deque = list_deque_time.mean(axis=1)

for i in range(K_LENGTH):
    only_list = result_only_list[i]
    list_deque = result_list_deque[i]
    if only_list > list_deque:
        print(f'only list is {list_deque/only_list:.3f}% fast')
    elif only_list < list_deque:
        print(f'only list is {only_list/list_deque:.3f}% fast')
    else:
        print('same speed')

[RESULT]

1
2
3
4
5
only list is -inf% fast
only list is -inf% fast
only list is 33.014% fast
only list is 97.508% fast
only list is 102.678% fast

더 빠른 곳에 대입하지 않고 바로 pop 하는 것이 무조건 빠르다. 다른 곳에 대입하는 것이 생각보다 엄청난 연산을 요하는 것 같다. 많은 연산시 더 빨라지지 않을 까 했는데 더 많을 경우 더 많은 수를 옮겨야 하기 때문에 훨씬 더 오래걸렸다.