Python의 빌트인 사전은 어떻게 구현됩니까?
python의 빌트인 사전 타입이 어떻게 구현되는지 아는 사람 있나요?해시테이블이라고 알고 있습니다만, 아직 확실한 답을 찾을 수 없었습니다.
여기 Python의 딕트에 관한 모든 것이 있습니다(아마도 누구보다도 많은 정보를 얻을 수 있었을 것입니다만, 답은 포괄적입니다).
Python 사전은 해시 테이블로 구현됩니다.
해시 테이블은 해시 충돌을 허용해야 합니다. 즉, 두 개의 서로 다른 키가 동일한 해시 값을 갖는 경우에도 테이블 구현은 키와 값의 쌍을 명확하게 삽입하고 가져오는 전략을 가져야 합니다.
Python 이 python
dict
는 오픈 어드레싱을 사용하여 해시 콜리전을 해결합니다(아래 참조). (dictobject.c:296-297 참조).은 메모리의같은 이므로 Python 해시 은 Python을 할 수 있습니다).
O(1)
★★★★★★★★★★★★★★★★」테이블의 각 슬롯에 저장할 수 있는 엔트리는 1개뿐입니다.이게 중요해요.
테이블 내의 각 엔트리는 실제로는 < hash 、 key 、 value >의 3가지 값의 조합입니다.이것은 C 구조체로 구현됩니다(dictobject.h:51-56 참조).
다음 그림은 Python 해시 테이블을 논리적으로 나타낸 것입니다.에서 래래 in in 。
0, 1, ..., i, ...
왼쪽은 해시 테이블에 있는 슬롯의 인덱스입니다(이것들은 설명을 위한 것일 뿐 테이블과 함께 저장되지는 않습니다!).# Logical model of Python Hash table -+-----------------+ 0| <hash|key|value>| -+-----------------+ 1| ... | -+-----------------+ .| ... | -+-----------------+ i| ... | -+-----------------+ .| ... | -+-----------------+ n| ... | -+-----------------+
새로운 dict가 초기화되면 8개의 슬롯으로 시작합니다.(dictobject.h:49 참조)
i
이는 키의 해시를 기반으로 합니다.에 CPython을 사용합니다.i = hash(key) & mask
서 (어디서)mask = PyDictMINSIZE - 1
「 」를 참조해 주세요). 번째 슬롯은 " " " 입니다.i
이 체크박스는 키의 해시에 따라 달라집니다.있는 그엔트리에 , 「」, 「」, 「」).
<hash|key|value>
만 ? ?????????????!?다른 엔트리의 해시(해시 충돌!)가 동일하기 때문일 가능성이 높습니다.슬롯이 사용되면 CPython(및 PyPy)은 해시 AND 키(비교)를 비교합니다.
==
가is
슬롯 내의 엔트리와 삽입하는 현재 엔트리의 해시 및 키(paramobject.c:337,344-345)를 비교합니다.양쪽이 일치하면 엔트리가 이미 존재한다고 간주하고 포기하고 다음 엔트리로 넘어갑니다.해시 또는 키가 일치하지 않으면 검색이 시작됩니다.프로브는 슬롯별로 슬롯을 검색하여 빈 슬롯을 찾는 것을 의미합니다. 말하면 한 갈 수
i+1, i+2, ...
첫 번째 사용 가능한 것(선형 프로브)을 사용합니다.그러나 코멘트에서 아름답게 설명되는 이유(dictobject.c:33-126 참조)로 인해 CPython은 랜덤 프로브를 사용합니다.랜덤 프로빙에서는 다음 슬롯이 의사 랜덤 순서로 선택됩니다.엔트리가 첫 번째 빈 슬롯에 추가됩니다.이 설명에서는 다음 슬롯을 선택하기 위해 사용되는 실제 알고리즘은 그다지 중요하지 않습니다(프로빙 알고리즘에 대해서는 dictobject.c:33-126 참조).중요한 것은 빈 슬롯이 최초로 발견될 때까지 슬롯을 조사하는 것입니다.룩업에서도 같은 일이 일어나 첫 번째 슬롯 i부터 시작합니다(키 해시에 의존합니다).해시 및 키가 모두 슬롯의 엔트리와 일치하지 않으면 일치하는 슬롯을 찾을 때까지 프로브를 시작합니다.모든 슬롯이 사용되면 장애가 보고됩니다.
데 b b b 。
dict
3분의 2가 차면 크기가 조정됩니다.이렇게 하면 검색 속도가 느려지지 않습니다.(dictobject.h:64-65 참조)
메모: 저는 어떻게 dict 내의 여러 엔트리가 동일한 해시 값을 가질 수 있는지에 대한 제 자신의 질문에 대한 답변으로 Python Dict 구현에 대해 조사했습니다.모든 연구가 이 질문에도 매우 관련이 있기 때문에 여기에 답변의 약간 편집된 버전을 올렸습니다.
Python의 빌트인 사전은 어떻게 구현됩니까?
간단한 코스는 다음과 같습니다.
- 해시 테이블입니다.(Python의 구현에 대한 자세한 내용은 아래를 참조하십시오.)
- Python 3.6의 새로운 레이아웃과 알고리즘이 이들을 만듭니다.
- 키 삽입에 의해 순서가 매겨져 있습니다.
- 공간을 적게 차지합니다.
- 퍼포먼스 면에서 거의 비용이 들지 않습니다.
- 또 다른 최적화로 공유 키를 받아쓰면(특수한 경우) 공간이 절약됩니다.
순서가 매겨진 측면은 Python 3.6에서는 비공식이지만(다른 구현에 따라갈 기회를 주기 위해), Python 3.7에서는 공식입니다.
Python의 사전은 해시 테이블입니다.
오랫동안, 그것은 정확히 이렇게 작동했다.Python은 8개의 빈 행을 미리 할당하고 해시를 사용하여 키와 값의 쌍을 고정할 위치를 결정합니다.예를 들어, 키의 해시가 001로 끝나는 경우, 키의 해시는 1(즉, 두 번째) 인덱스에 고정됩니다(아래 예시와 같습니다).
<hash> <key> <value>
null null null
...010001 ffeb678c 633241c4 # addresses of the keys and values
null null null
... ... ...
각 행은 64비트 아키텍처에서는 24바이트, 32비트에서는 12바이트를 차지합니다.(컬럼 헤더는 여기서는 라벨일 뿐 실제로는 메모리에 존재하지 않습니다.)
해시가 기존 키의 해시와 동일하게 종료된 경우 이는 충돌이며 키와 값의 쌍이 다른 위치에 고정됩니다.
5개의 키 값을 저장한 후 다른 키와 값의 쌍을 추가할 때 해시 충돌 확률이 너무 높기 때문에 사전 크기가 두 배로 커집니다.64비트 프로세스에서는 크기 조정 전에 72바이트가 비어 있습니다.그 이후에는 10개의 빈 행으로 인해 240바이트가 낭비됩니다.
이 작업에는 많은 공간이 소요되지만 조회 시간은 상당히 일정합니다.키 비교 알고리즘은 해시를 계산하고 예상되는 위치로 이동하여 키의 ID를 비교하는 것입니다. 같은 개체일 경우 동일한 값을 갖습니다.해시 값을 비교하지 않으면 동일하지 않으면 동일하지 않습니다.그렇지 않으면 마지막으로 동일한 키를 비교하고 동일한 경우 값을 반환합니다.평등에 대한 최종 비교는 매우 느릴 수 있지만, 이전의 체크는 보통 최종 비교를 단축하기 때문에 검색이 매우 빠릅니다.
충돌로 인해 속도가 느려지고 공격자가 이론적으로 해시 충돌을 사용하여 서비스 거부 공격을 수행할 수 있으므로 새로운 Python 프로세스마다 다른 해시를 계산하도록 해시 함수의 초기화를 랜덤화했습니다.
위에서 설명한 낭비된 공간은 사전의 구현을 수정하고 사전이 삽입으로 주문되는 흥미로운 신기능을 제공하게 되었습니다.
새로운 콤팩트 해시 테이블
대신 삽입 인덱스에 어레이를 미리 할당하는 것으로 시작합니다.
첫 번째 키와 값의 쌍이 두 번째 슬롯에 있기 때문에 다음과 같이 인덱스를 작성합니다.
[null, 0, null, null, null, null, null, null]
삽입 순서에 따라 테이블이 채워집니다.
<hash> <key> <value>
...010001 ffeb678c 633241c4
... ... ...
따라서 키를 조회할 때 해시를 사용하여 예상되는 위치를 확인합니다(이 경우 어레이의 인덱스1로 직행하고 나서 해시 테이블 내의 인덱스(예를 들어 인덱스0)로 이동하여 키가 동일한지 확인하고(앞에서 설명한 알고리즘 사용) 값을 반환합니다.
기존 구현에 비해 상당한 공간을 절약하고 삽입 순서를 유지할 수 있는 반면, 검색 시간은 일정하게 유지되며, 경우에 따라서는 약간의 속도 저하와 이점도 있습니다.낭비되는 공간은 인덱스 배열의 늘바이트뿐입니다.
Raymond Hettinger는 2012년 12월에 python-dev에서 이를 도입했습니다.파이썬 3.6에서 드디어 CPython에 들어갔습니다.Python의 다른 구현이 따라잡을 수 있도록 3.6의 구현 세부사항으로 간주되었습니다.
공유 키
공간을 절약하기 위한 또 다른 최적화 방법은 키를 공유하는 구현입니다.따라서 공간을 모두 차지하는 중복 사전을 보유하는 대신 공유 키와 키의 해시를 재사용하는 사전을 보유하게 됩니다.다음과 같이 생각할 수 있습니다.
hash key dict_0 dict_1 dict_2...
...010001 ffeb678c 633241c4 fffad420 ...
... ... ... ... ...
64비트 머신의 경우 이를 통해 추가 사전당 키당 최대 16바이트를 절약할 수 있습니다.
커스텀 오브젝트 및 대체 공유 키
이러한 공유 키 딕트는 커스텀오브젝트용으로 사용됩니다.__dict__
이런 행동을 취하기 위해서는 제 생각엔 제 생각엔, 제 생각엔, 제 생각엔, 제 생각에는,__dict__
다음 오브젝트를 인스턴스화하기 전에(PEP 412 참조).즉, 에 있는 모든 Atribute를 할당해야 합니다.__init__
또는__new__
그렇지 않으면 공간 절약을 얻지 못할 수 있습니다.
단, 그 시점에서 자신의 속성을 모두 알고 있는 경우,__init__
실행할 수 있습니다.__slots__
당신의 목적을 위해, 그리고 보증합니다.__dict__
(부모가 이용할 수 없는 경우) 전혀 생성되지 않거나__dict__
단, 예측된 Atribute가 슬롯에 저장되어 있음을 보증합니다.에 대한 자세한 내용을 참조해 주세요.__slots__
여기서 제 답을 보세요.
다음 항목도 참조하십시오.
- PEP 509 - 개인 버전을 dict에 추가합니다.
- PEP 468 -- 순서 유지
**kwargs
함수에서. - PEP 520 -- 클래스 속성 정의 순서 유지
- PyCon 2010: 마이트 사전 - 브랜든 로즈
- PyCon 2017: 더 이븐 마이트리어 사전 - 브랜든 로즈
- PyCon 2017: 모던 Python 사전 수십 개의 훌륭한 아이디어의 융합 - Raymond Hettinger
- dictobject.c - C에서의 CPython의 실제 dict 실장.
Python 사전은 개방형 주소 지정을 사용합니다(아름다운 코드 내 참조).
NB! 개방형 주소 지정, 즉 폐쇄형 해시는 위키피디아에서 언급한 바와 같이 반대되는 개방형 해시와 혼동해서는 안 됩니다.
오픈 어드레싱이란 dict가 어레이 슬롯을 사용하는 것을 의미합니다.dict에서 객체의 프라이머리 위치가 지정되면 오브젝트의 해시 값이 관여하는 "퍼터베이션" 방식을 사용하여 동일한 어레이 내의 다른 인덱스에서 객체의 위치를 찾습니다.
언급URL : https://stackoverflow.com/questions/327311/how-are-pythons-built-in-dictionaries-implemented
'sourcecode' 카테고리의 다른 글
iPhone 및 Android의 JavaScript에서 손가락 스와이프 감지 (0) | 2022.09.18 |
---|---|
'크기 조정' 이벤트가 끝날 때까지 기다렸다가 조치를 취하는 방법은 무엇입니까? (0) | 2022.09.17 |
MariaDB 10.1 암호화 확인 (0) | 2022.09.17 |
두 Joda-Time DateTimes 간의 차이를 분 단위로 찾는 방법 (0) | 2022.09.17 |
속도 향상 인텔리J-Idea (0) | 2022.09.17 |