짧은 파이썬 목록을 추가하기위한 관용구 문은 무엇입니까?


543

list.append()목록 끝에 추가하기위한 확실한 선택입니다. 다음 은 실종에 대한 합리적인 설명 입니다 list.prepend(). 내 목록이 짧고 성능 문제가 무시할 만하다고 가정하면

list.insert(0, x)

또는

list[0:0] = [x]

관용?

답변:


784

s.insert(0, x)형태가 가장 일반적이다.

그래도 볼 때마다 목록 대신 collections.deque 를 사용하는 것이 좋습니다.


9
"하지만 볼 때마다 목록 대신 collections.deque를 사용하는 것이 좋습니다." 왜 이런거야?
Matt M.

6
@MattM. 리스트의 앞에 삽입하면, 파이썬은 다른 모든 항목을 한 칸 앞으로 이동시켜야합니다.리스트는 "앞에 공백을 만들 수 없습니다". collections.deque (double ended queue)는 "앞 공간 만들기"를 지원하며이 경우 훨씬 빠릅니다.
fejfo

265

기능적인 방법으로 갈 수 있다면 다음이 분명합니다.

new_list = [x] + your_list

물론 당신은 삽입하지 않은 x으로 your_list오히려 당신과 함께 새 목록 생성 한, x그것에 preprended을.


45
알다시피, 그것은 목록 앞에 붙지 않습니다. 새로운 목록을 만들고 있습니다. 따라서 질문을 전혀 만족시키지 않습니다.
Chris Morgan

112
질문을 만족 시키지는 않지만 반올림하며 이것이이 웹 사이트의 목적입니다. 의견을 고맙다면 당신은 옳다. 그러나 사람들이 이것을 검색하면 이것을 보는 것이 도움이된다.
dave4jr

2
또한 목록을 목록에 추가하려면 삽입을 사용하여 예상대로 작동하지 않습니다. 그러나이 방법은 않습니다!
gota

89

짧은 파이썬 목록을 추가하기위한 관용구 문은 무엇입니까?

일반적으로 Python의 목록 앞에 반복적으로 추가하고 싶지 않습니다.

그것의 경우 짧은 , 당신이 그것을 많이 안하고 ... 다음 확인을 클릭합니다.

list.insert

list.insert이 방법을 사용할 수 있습니다.

list.insert(0, x)

그러나 파이썬에서는 a list가 포인터의 배열이므로 파이썬은 목록의 모든 포인터를 가져 와서 포인터를 첫 번째 슬롯에 객체에 삽입하기 위해 하나씩 아래로 이동해야하므로 비효율적입니다. 당신이 요청 한대로 오히려 짧은 목록.

CPython 소스 의 스 니펫 다음과 같습니다. 여기서 알 수 있듯이 배열의 끝에서 시작하여 모든 삽입마다 하나씩 아래로 이동합니다.

for (i = n; --i >= where; )
    items[i+1] = items[i];

요소 앞에 붙일 수있는 컨테이너 / 목록을 원한다면 연결된 목록이 필요합니다. 파이썬은 이중 연결리스트를 가지고 있는데, 처음에는 빠르게 삽입하고 끝낼 수 deque있습니다.

deque.appendleft

A collections.deque에는 목록의 많은 방법이 있습니다. list.sort만들기 예외 인 deque에 대한 결정적 전적으로 Liskov의 대용은 list.

>>> set(dir(list)) - set(dir(deque))
{'sort'}

deque또한 갖는다 appendleft방법 (뿐만 아니라 popleft). 는 deque이중 종료 큐와 이중 연결리스트이다 -에 상관없이 길이는 항상 preprend 뭔가에 동일한 시간이 소요됩니다. 큰 O 표기법에서 O (1) 대 목록의 O (n) 시간. 사용법은 다음과 같습니다.

>>> import collections
>>> d = collections.deque('1234')
>>> d
deque(['1', '2', '3', '4'])
>>> d.appendleft('0')
>>> d
deque(['0', '1', '2', '3', '4'])

deque.extendleft

또한 deque의 extendleft방법 이 관련이 있으며 반복적으로 앞에 붙습니다.

>>> from collections import deque
>>> d2 = deque('def')
>>> d2.extendleft('cba')
>>> d2
deque(['a', 'b', 'c', 'd', 'e', 'f'])

각 요소는 한 번에 하나씩 추가되므로 순서를 효과적으로 반대로 바꿉니다.

list대 성능deque

먼저 우리는 몇 가지 반복적 인 접두사로 설정합니다.

import timeit
from collections import deque

def list_insert_0():
    l = []
    for i in range(20):
        l.insert(0, i)

def list_slice_insert():
    l = []
    for i in range(20):
        l[:0] = [i]      # semantically same as list.insert(0, i)

def list_add():
    l = []
    for i in range(20):
        l = [i] + l      # caveat: new list each time

def deque_appendleft():
    d = deque()
    for i in range(20):
        d.appendleft(i)  # semantically same as list.insert(0, i)

def deque_extendleft():
    d = deque()
    d.extendleft(range(20)) # semantically same as deque_appendleft above

그리고 성능 :

>>> min(timeit.repeat(list_insert_0))
2.8267281929729506
>>> min(timeit.repeat(list_slice_insert))
2.5210217320127413
>>> min(timeit.repeat(list_add))
2.0641671380144544
>>> min(timeit.repeat(deque_appendleft))
1.5863927800091915
>>> min(timeit.repeat(deque_extendleft))
0.5352169770048931

deque가 훨씬 빠릅니다. 목록이 길어질수록 deque가 더 잘 수행 될 것으로 기대합니다. deque를 사용할 수 있다면 extendleft아마도 최고의 성능을 얻을 것입니다.


57

누군가 나처럼이 질문을 찾으면 제안 된 방법에 대한 성능 테스트가 있습니다.

Python 2.7.8

In [1]: %timeit ([1]*1000000).insert(0, 0)
100 loops, best of 3: 4.62 ms per loop

In [2]: %timeit ([1]*1000000)[0:0] = [0]
100 loops, best of 3: 4.55 ms per loop

In [3]: %timeit [0] + [1]*1000000
100 loops, best of 3: 8.04 ms per loop

보시다시피 insert슬라이스 할당은 명시 적 추가보다 거의 두 배 빠르며 결과는 매우 가깝습니다. 로 레이몬드 Hettinger는 지적은 insert일반적인 옵션이며 나는 개인적으로 목록에 앞에 추가에이 방법을 선호합니다.


11
이 테스트에서 빠진 한 가지는 복잡성입니다. 처음 두 옵션은 일정한 복잡성을 갖지만 (목록에 더 많은 요소가있을 때 느려지지 않음) 세 번째 옵션은 선형 복잡성을 갖습니다 (목록에있는 요소의 양에 따라 느려집니다). 전체 목록을 복사해야합니다. 목록에 더 많은 요소가 있으면 결과가 훨씬 나빠질 수 있습니다.
Dakkaron

6
@Dakkaron 나는 당신이 그것에 대해 잘못 생각합니다. list.insert의 선형 복잡도를 인용 한 소스가 꽤 있습니다. 예를 들어이 멋진 표 는 질문자가 연결된 합리적인 설명에 의해 암시됩니다. CPython이 처음 두 경우의 목록에서 메모리의 각 요소를 다시 할당한다고 생각하므로이 세 가지 모두 선형 복잡성을 가질 수 있습니다. 실제로 코드를 보거나 직접 테스트하지는 않았으므로 해당 소스가 잘못된 경우 죄송합니다. Collections.deque.appendleft는 당신이 말하는 선형 복잡성을 가지고 있습니다.
TC Proctor

@Dakkaron은 사실이 아니며, 모두 복잡합니다. 비록 .insert[0:0] = [0]작업 에 장소 , 그들은 여전히에있는 전체 버퍼를 다시 할당 할 수 있습니다.
juanpa.arrivillaga

이 벤치 마크는 나쁘다. 초기 목록은 타이밍 자체의 일부가 아닌 별도의 설정 단계에서 작성해야합니다. 마지막은 1000001 길이의 새 목록을 생성하므로 다른 두 가지 변경 가능한 내부 버전과 비교하면 사과와 오렌지입니다.
wim
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.