Python 3.6 이상에서 사전이 주문됩니까?


467

사전은 이전 화신과 달리 Python 3.6 (최소한 CPython 구현)에서 주문됩니다. 이것은 실질적인 변화처럼 보이지만 문서 의 짧은 단락 일뿐 입니다. 언어 기능이 아닌 CPython 구현 세부 사항으로 설명되지만 앞으로 이것이 표준이 될 수 있음을 암시합니다.

요소 사전을 유지하면서 새로운 사전 구현이 이전 사전보다 어떻게 더 잘 수행됩니까?

다음은 설명서의 텍스트입니다.

dict()이제 PyPy가 개척 한 "소형"표현을 사용합니다 . 새로운 dict ()의 메모리 사용량은 Python 3.5에 비해 20 %에서 25 % 사이입니다. PEP 468 (함수에서 ** kwargs의 순서 유지)이 구현됩니다. 이 새로운 구현의 순서 유지 측면은 구현 세부 사항으로 간주되며 의존해서는 안됩니다 (향후 변경 될 수 있지만 언어 사양을 변경하기 전에 몇 가지 릴리스에 대해 언어 로이 새로운 dict 구현을 갖는 것이 바람직합니다 현재와 ​​미래의 모든 파이썬 구현에 대한 순서 유지 의미론을 강제하기 위해; 이것은 또한 임의의 반복 순서가 여전히 유효한 이전 버전의 언어와의 하위 호환성을 유지하는 데 도움이됩니다 (예 : Python 3.5). (INADA Naoki가 제공 한문제 27350 . Raymond Hettinger가 처음 제안한 아이디어 .)

2017 년 12 월 업데이트 : Python 3.7에 대한 게재 dict신청서 유지 보장


2
Python-Dev 메일 링리스트에서이 스레드를 참조하십시오 : mail.python.org/pipermail/python-dev/2016-September/146327.html 보지 못한 경우; 기본적으로 이러한 주제에 대한 토론입니다.
mgc

1
kwargs가 이제 주문되어야하고 (좋은 생각입니다) kwargs가 OrderedDict가 아닌 dict이면, 문서가 달리 말하고 있지만 dict 키가 이후 버전의 Python에서 계속 정렬되어 있다고 가정 할 수 있습니다.
Dmitriy Sintsov

4
@DmitriySintsov 아니오, 그 가정을하지 마십시오. 이것은 주문 보존 기능을 정의하는 PEP를 작성하는 동안 제기 된 문제였으며 **kwargs사용 된 문구는 외교적입니다. **kwargs함수 서명에서 이제 삽입 순서 보존 매핑이 보장됩니다 . 그들은 다른 구현이 dict을 지시하고 ( 내부적으로 사용하도록 ) 강제하지 않고 순서가 없다는 사실에 의존해서는 안된다는 신호를 보내는 방법으로 매핑 이라는 용어를 사용했습니다 . OrderedDictdict
Dimitris Fasarakis Hilliard 2012

7
Raymond Hettinger 의 좋은 비디오 설명
Alex

1
@wazoox, 해시 맵의 순서와 복잡성은 변경되지 않았습니다. 공간이 줄어들면 해시 맵이 더 작아지고 저장된 공간은 보조 배열보다 (보통?) 더 큽니다. 더 빠르고, 더 작고, 주문했다 – 당신은 모든 3를 선택하게된다.
John La Rooy

답변:


510

Python 3.6 이상에서 사전이 주문됩니까?

그것들은 삽입 순서가되어있다 [1] . Python 3.6부터 CPython Python 구현에서 사전 은 삽입 된 항목의 순서를 기억합니다 . 이것은 Python 3.6에서 구현 세부 사항으로 간주됩니다 . 다른 파이썬 구현 OrderedDict에서 보장 되는 삽입 순서 (및 기타 정렬 된 동작 [1] ) 를 원할 경우 사용해야 합니다 .

Python 3.7부터는 더 이상 구현 세부 사항이 아니며 언어 기능이됩니다. GvR의 python-dev 메시지에서 :

그렇게 만들어. "사전 삽입 순서 유지"는 판결입니다. 감사!

이것은 단순히 당신이 그것에 의존 할 수 있다는 것을 의미 합니다 . 파이썬의 다른 구현은 파이썬 3.7의 적합한 구현이 되려면 삽입 순서 사전을 제공해야합니다.


어떻게 파이썬 않는 3.6사전 구현은 더 잘 수행 [2] 이전보다가 그대로 유지하면서 요소 순서를?

본질적으로 두 개의 배열유지함으로써 .

  • 첫 번째 배열 인 에는 사전에 대한 dk_entries항목 ( 유형PyDictKeyEntry )을 삽입 된 순서대로 보유합니다 . 보존 순서는 새 항목이 항상 끝에 삽입되는 추가 전용 배열 (삽입 순서)로 이루어집니다.

  • 두 번째 dk_indicesdk_entries배열 의 인덱스 (즉,에서 해당 항목의 위치를 ​​나타내는 값 dk_entries)를 보유합니다. 이 배열은 해시 테이블 역할을합니다. 키가 해시되면 키가 저장된 인덱스 중 하나로 연결되고 dk_indices해당 항목은 색인화하여 가져옵니다 dk_entries. 인덱스 만이 유지되므로, 이러한 배열의 형태가 사전의 전체 크기 (레인 징의 타입에 따라 int8_t( 1바이트)로 int32_t/ int64_t( 4/ 8바이트)에 32/ 64비트 빌드)

이전 구현에서는 유형 PyDictKeyEntry과 크기 의 희소 배열을 dk_size할당해야했습니다. 불행히도, 성능상의 이유로 어레이가 2/3 * dk_size가득 차는 것을 허용하지 않았기 때문에 빈 공간이 많이 생겼습니다 . (그리고 빈 공간에는 여전히 크기 가 있습니다!).PyDictKeyEntry

단지 때문에 지금 그렇지 않다 필요한 항목의 입력은 희소 배열 (삽입 된 것)에 저장된다 intX_t( X딕셔너리 크기에 따라) 2/3 * dk_size의 전체 유지된다. 빈 공간이 유형에서 PyDictKeyEntry로 변경되었습니다 intX_t.

따라서, 희소 한 유형의 배열을 생성하는 PyDictKeyEntry것은 ints 를 저장하기위한 희소 배열보다 훨씬 더 많은 메모리를 요구 합니다.

관심이 있다면이 기능에 관한 Python-Dev 의 전체 대화 볼 수 있습니다 .


Raymond Hettinger의 원래 제안에서 , 사용 된 데이터 구조의 시각화를 통해 아이디어의 요점을 파악할 수 있습니다.

예를 들어 사전은 다음과 같습니다.

d = {'timmy': 'red', 'barry': 'green', 'guido': 'blue'}

현재 [keyhash, key, value]로 저장됩니다 :

entries = [['--', '--', '--'],
           [-8522787127447073495, 'barry', 'green'],
           ['--', '--', '--'],
           ['--', '--', '--'],
           ['--', '--', '--'],
           [-9092791511155847987, 'timmy', 'red'],
           ['--', '--', '--'],
           [-6480567542315338377, 'guido', 'blue']]

대신, 데이터는 다음과 같이 구성되어야합니다.

indices =  [None, 1, None, None, None, 0, None, 2]
entries =  [[-9092791511155847987, 'timmy', 'red'],
            [-8522787127447073495, 'barry', 'green'],
            [-6480567542315338377, 'guido', 'blue']]

시각적으로 볼 수 있듯이 원래 제안에서 충돌을 줄이고 조회 속도를 높이기 위해 많은 공간이 기본적으로 비어 있습니다. 새로운 접근 방식을 사용하면 인덱스에서 실제 필요한 위치에 스 퍼니스를 이동하여 필요한 메모리를 줄일 수 있습니다.


[1] : OrderedDict가 있으면 "ordered"는 dict개체 가 제공하지 않는 추가 동작을 나타 내기 때문에 "inserted ordered" 가 아니라 "ordered" 가 아닙니다 . OrderedDict는 가역적이며 순서에 민감한 방법을 제공하며 주로 순서에 민감한 등식 테스트 ( ==, !=)를 제공합니다. dict현재 이러한 행동 / 방법을 제공하지 않습니다.


[2] : 새로운 딕셔너리 구현은보다 간결하게 설계되어보다 나은 메모리 성능을 제공합니다 . 이것이 주요 이점입니다. 현명하게도, 그 차이는 그다지 급격하지는 않습니다. 새로운 dict이 약간의 회귀 ( : 키 조회)를 유발할 수있는 반면, 다른 경우 (반복 및 크기 조정이 떠오를 경우)에는 성능 향상이 있어야합니다.

전반적으로, 특히 실제 상황에서 사전의 성능은 도입 된 소형화로 인해 향상됩니다.


15
항목을 제거하면 어떻게됩니까? 는 IS entries목록 크기를 조정할? 아니면 빈 공간이 유지됩니까? 아니면 때때로 압축됩니까?
njzk2

18
@ njzk2 항목이 제거되면 해당 색인이 DKIX_DUMMY값으로 대체되고 배열 -2의 항목이 로 대체됩니다 . 삽입이 수행되면 새 값이 항목 배열에 추가됩니다. 아직 식별 할 수 없었습니다. 그러나 인덱스가 임계 값 을 초과하여 채워질 때 확실히 크기 조정이 수행됩니다. 이로 인해 많은 항목이 존재 하면 커지는 대신 축소 될 수 있습니다. entryNULL2/3DUMMY
Dimitris Fasarakis Hilliard

3
@Chris_Rands Nope, 내가 본 유일한 회귀 분석은 Victor메시지 에서 추적기에 있습니다. 그 마이크로 벤치 마크 이외에, 나는 실제 작업 부하에서 심각한 속도 차이를 나타내는 다른 문제 / 메시지를 보지 못했습니다. 새로운 dict이 약간의 회귀 (예 : 키 조회)를 유발할 수있는 반면, 다른 경우 (반복 및 크기 조정을 염두에두면) 성능이 향상 될 수 있습니다.
Dimitris Fasarakis Hilliard

3
크기 조정 부분의 수정 : 사전은 항목을 삭제할 때 크기가 조정되지 않으며 다시 삽입 할 때 다시 계산됩니다. 따라서 dict을 사용 d = {i:i for i in range(100)}하여 .pop삽입하지 않고 모든 항목을 삽입하지 않으면 크기가 변경되지 않습니다. 다시 추가 d[1] = 1하면 적절한 크기가 계산되고 dict 크기가 조정됩니다.
Dimitris Fasarakis Hilliard 17 :

6
@Chris_Rands 나는 그것이 머무르고 있다고 확신합니다. 문제는 ' dict주문받는' 에 대한 담요 진술을 제거하기 위해 답변을 변경 한 이유 dict는 의미가 주문되지 않습니다 OrderedDict. 주목할만한 문제는 평등입니다. dictS는 순서를 구분을 ==, OrderedDict의 순서에 민감한 사람이있다. 을 덤프 OrderedDict하고 dicts순서에 민감한 비교를 갖도록 변경 하면 이전 코드에서 많은 손상이 발생할 수 있습니다. OrderedDicts에 대해 변경 될 수있는 유일한 것은 구현입니다.
Dimitris Fasarakis Hilliard

66

아래는 첫 번째 원래 질문에 대한 답변입니다.

나는 사용해야 dict또는 OrderedDict파이썬 3.6?

나는 문서 의이 문장이 실제로 귀하의 질문에 대답하기에 충분하다고 생각합니다

이 새로운 구현의 순서 유지 측면은 구현 세부 사항으로 간주되며 이에 의존해서는 안됩니다.

dict은 명시 적으로 순서가 지정된 컬렉션을 의미하지 않으므로 일관성을 유지하고 새로운 구현의 부작용에 의존하지 않으려면를 사용해야합니다 OrderedDict.

코드를 미래의 증거로 만드십시오 :)

여기에 대한 논쟁이 있습니다 .

편집 : 파이썬 3.7는 기능으로이 유지됩니다 참조


1
그것이 실제 기능이 아니라 구현 세부 사항만을 의미한다면 문서에 넣지 않아도됩니다.
xji

3
나는 당신의 편집주의에 대해 확신하지 못한다. 보증은 Python 3.7에만 적용되므로, Python 3.6에 대한 조언은 변경되지 않은 것으로 가정합니다. 즉, dicts는 CPython에서 주문되지만이를 고려하지 않습니다.
Chris_Rands

25

업데이트 : Guido van Rossum 은 메일 링리스트dict 에서 모든 Python 구현에서 Python 3.7부터 삽입 순서를 유지해야 한다고 발표했습니다 .


2
이제 키 순서가 공식 표준입니다. OrderedDict의 목적은 무엇입니까? 아니면 이제 중복입니까?
Jonny Waffles

2
OrderedDict는 move_to_end메소드가 있고 그 동등성이 순서에 민감하기 때문에 중복되지 않을 것입니다 : docs.python.org/3/library/… . Jim Fasarakis Hilliard의 답변에 대한 메모를 참조하십시오.
fjsj

@JonnyWaffles 짐의 대답이 Q & A를 참조 stackoverflow.com/questions/50872498/...
Chris_Rands

3
2.7 및 3.6 / 3.7 +에서 코드를 동일하게 실행하려면 OrderedDict
boatcoder

3
아마 보안상의 이유로 자신의 지시를 번거롭게하고 싶어하는 사람들을 위해 곧 "정렬되지 않은 사전"이있을 것입니다; p
ZF007

9

위의 토론에 추가하고 싶었지만 언급 할만한 평판이 없습니다.

Python 3.8은 아직 출시되지 않았지만 reversed()사전에 대한 기능 도 포함합니다 (와 다른 차이점 제거) OrderedDict.

딕 트뷰와 dictviews는 이제 reversed ()를 사용하여 역 삽입 순서로 반복 가능합니다. (pyo 33462의 Rémi Lapeyre 제공) Python 3.8의 새로운 기능보기

평등 연산자 또는 다른 기능에 대한 언급이 OrderedDict없으므로 완전히 동일하지 않습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.