Program Tip

사전에서 비교할 때 키를 선택하기 위해 Python 3.5가 선택한 사항

programtip 2020. 12. 4. 20:20
반응형

사전에서 비교할 때 키를 선택하기 위해 Python 3.5가 선택한 사항


다음과 같이 사전을 구성 할 때 :

dict = { True: 'yes', 1: 'No'}

대화 형 Python 인터프리터에서 실행하면 dict가 다음과 같이 표시됩니다.

dict = {True: 'No'}

I의 값을 이해 True하고 1숫자 형식을 비교하면, 좁은 타입 (부울 정수의 자식) 다른 유형으로 확대되기 때문에, 강압 형 때문에 동일하다. 그래서 나는 우리가 입력 한 문서에서 이해되는 True == 1파이썬 변환 True에를 1와 비교합니다.

내가 이해하지 못하는 것은 True이 대신 키로 선택된 이유 입니다 1.

내가 뭔가를 놓치고 있습니까?


사전은 해시 테이블로 구현되며 여기에 키 / 값을 추가 할 때 두 가지 중요한 개념 인 해싱같음이 있습니다.

특정 키 / 값을 삽입하기 위해 Python은 먼저 해시 값을 계산합니다 . 이 해시 값은 Python이 먼저 키 / 값을 입력해야하는 테이블의 행을 결정하는 데 사용됩니다.

해시 테이블의 행이 비어 있으면 좋습니다. 새 키 / 값을 사전에 삽입하여 빈 행을 채울 수 있습니다.

그러나 해당 행에 이미 무언가가있는 경우 Python은 키가 같은지 테스트해야합니다. 키가 같으면 (사용 ==) 동일한 키로 간주되며 Python은 해당 행의 해당 값을 업데이트하면됩니다.

(키가 같지 않은 경우 Python은 키를 찾거나 빈 행에 도달 할 때까지 테이블의 다른 행을 살펴 보지만이 질문과는 관련이 없습니다.)


를 작성할 때 {True: 'yes', 1: 'No'}Python에 새 사전을 만든 다음 두 개의 키 / 값 쌍으로 채우도록 지시하는 것입니다. 왼쪽에서 오른쪽으로 처리됩니다. True: 'yes'그런 다음 1: 'No'.

우리는 hash(True)1 같습니다. 키 True는 해시 테이블의 행 1에 들어가고 문자열 'yes'은 그 값입니다.

다음 쌍의 경우 Python은이 hash(1)값도 1 인 것을보고 테이블의 1 행을 봅니다. 이미 거기에 무언가가 있으므로 이제 Python은 키가 같은지 확인합니다. 우리는 한 1 == True지금 1과 같은 열쇠가 될 것으로 간주됩니다 True에 대응하는 값이 문자열로 변경되고 정도 'No'.

결과적으로 하나의 항목이있는 사전이 생성됩니다 {True: 'No'}..


CPython 3.5의 내장을 들여다보고 사전을 만드는 것이 표면 Python 수준 아래에서 어떻게 보이는지 확인하려면 여기에 더 자세한 내용이 있습니다.

  • Python 코드 {True: 'yes', 1: 'No'}는 토큰으로 구문 분석되어 컴파일러에 제공됩니다. 구문이 주어지면 Python은 중괄호 안의 값을 사용하여 사전을 만들어야한다는 것을 알고 있습니다. 4 개의 값을 가상 머신의 스택 ( LOAD_CONST) 에로드 한 다음 사전 ( BUILD_MAP) 을 빌드하는 바이트 코드 가 대기열에 추가됩니다.

  • 4 개의 상수 값이 보이는 순서대로 스택의 맨 위에 푸시됩니다.

    'No'
    1
    'yes'
    True
    
  • BUILD_MAP그런 다음 opcode 가 인수와 함께 호출됩니다 2(Python은 두 개의 키 / 값 쌍을 계산 함). 이 opcode는 실제로 스택의 항목에서 사전을 생성합니다. 그것은 모양 :

    TARGET(BUILD_MAP) {
        int i;
        PyObject *map = _PyDict_NewPresized((Py_ssize_t)oparg);
        if (map == NULL)
            goto error;
        for (i = oparg; i > 0; i--) {
            int err;
            PyObject *key = PEEK(2*i);
            PyObject *value = PEEK(2*i - 1);
            err = PyDict_SetItem(map, key, value);
            if (err != 0) {
                Py_DECREF(map);
                goto error;
            }
        }
    
        while (oparg--) {
            Py_DECREF(POP());
            Py_DECREF(POP());
        }
        PUSH(map);
        DISPATCH();
    }
    

여기에서 세 가지 주요 단계는 다음과 같습니다.

  1. 빈 해시 테이블은 _PyDict_NewPresized. 작은 사전 (이 경우 2 개와 같은 몇 개의 항목)에는 8 개의 행이있는 테이블이 필요합니다.

  2. for루프 (이 경우) (2)에서 시작하여 0으로 카운트 다운, 입력하는 PEEK(n)매크로 있다는 스택 아래 n 번째 항목 포인트. 따라서 루프의 첫 번째 반복에서

PyObject *key = PEEK(2*2);       /* item 4 down the stack */  
PyObject *value = PEEK(2*2 - 1); /* item 3 down the stack */

이 수단 *keyTrue*value것이다 'yes'내지 제 루프. 두 번째에는 1'No'.

  1. PyDict_SetItem현재를 넣어 각 루프에서 호출 *key하고 *value사전에. 이것은 당신이 쓸 때 호출되는 동일한 함수입니다 dictionary[key] = value. 키의 해시를 계산하여 해시 테이블에서 먼저 찾을 위치를 파악한 다음 필요한 경우 키를 해당 행의 기존 키와 비교합니다 (위에서 설명한대로).

기본 전제는 - True1같은 해시가 서로 동일 - (- 해시 충돌 성능이 저하 할 수 있지만 같은 해시 기술적으로 inequal 객체)가 해시 테이블에 별도의 키가 될 수없는 이유가 있습니다.

>>> True == 1
True
>>> hash(1)
1
>>> hash(True)
1

이제 바이트 코드를 살펴 보겠습니다.

import dis
dis.dis("Dic = { True: 'yes', 1: 'No'}")

이것은 다음을 인쇄합니다.

      0 LOAD_CONST               0 (True)
      3 LOAD_CONST               1 ('yes')
      6 LOAD_CONST               2 (1)
      9 LOAD_CONST               3 ('No')
     12 BUILD_MAP                2
     15 STORE_NAME               0 (Dic)
     18 LOAD_CONST               4 (None)
     21 RETURN_VALUE

기본적으로 dict 리터럴이 키와 값으로 토큰 화되고 스택으로 푸시됩니다. 그 후 BUILD_MAP 2두 쌍의 (키, 값)을 사전으로 변환합니다.

Most likely order of data on stack (which seems to be determined by order of keys in dict literal) and implementation details of BUILD_MAP decides on resulting dict keys and values.

It seems like key-value assignments are done in order defined in dict literal. Assignment behaves same as d[key] = value operation, so it's basically:

  • if key is not in dict (by equality): add key do dict
  • store value under key

Given {True: 'yes', 1: 'No'}:

  1. Start with {}
  2. Add (True, 'yes')

    1. True is not in dict -> (True, hash(True)) == (True, 1) is new key in dict
    2. Update value for key equal to 1 to 'yes'
  3. Add (1, 'no')

    1. 1 is in dict (1 == True) -> there is no need for new key in dictionary
    2. Update value for key equal to 1 (True) with value 'no'

Result: {True: 'No'}

As I commented, I do not know if this is guarranted by Python specs or if it is just CPython implementation-defined behavior, it may differ in other interpreter implementations.


True and 1 are different objects, but they both have the same value:

>>> True is 1
False
>>> True == 1
True

This is similar to two strings that may have the same value, but are stored in different memory locations:

>>> x = str(12345)
>>> y = str(12345)
>>> x == y 
True
>>> x is y
False

First one item is added to the dictionary; then when the other is added, that value already exists as a key. So the key is updated, key values are unique.

>>> {x: 1, y: 2}
{"12345": 2}

If the key is already present in the dictionary, it does not override the key only the value associated.

I believe that writing x = {True:"a", 1:"b"} is along the lines of:

x = {}
x[True] = "a"
x[1] = "b"

and by the time it reaches x[1] = "b" the key True is already in the dict so why change it? why not just override the associated value.


The ordering seems to be the reason. Code example:

>>> d = {True: 'true', 1: 'one'}
>>> d
{True: 'one'}
>>> d = {1: 'one', True: 'true'}
>>> d
{1: 'true'}

It's an ambiguous statement.

Logic: d = { True: 'no', 1: 'yes'}

When python evaluates the expression, it does it sequentially, so it's equivalent to this.

d = dict() d[True] = 'no' d[1] = 'yes'

The constant True is the key, but it evaluates to 1, so you're just setting the value for the key twice.

참고URL : https://stackoverflow.com/questions/37164127/choice-made-by-python-3-5-to-choose-the-keys-when-comparing-them-in-a-dictionary

반응형