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


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.