Python 문자열 대상 실현 원리 상세 설명

Python 세계 에서 대상 을 두 가지 로 나 누 었 습 니 다.하 나 는 정수,정수 대상 이 정의 할 때 사용 하 는 메모리 공간 크기 를 확인 할 수 있 고 다른 하 나 는 길 어 지 는 대상 입 니 다.대상 이 정의 할 때 얼마 인지 모 릅 니 다.예 를 들 어 str,list,set,dict 등 입 니 다.

>>> import sys
>>> sys.getsizeof(1000)
28
>>> sys.getsizeof(2000)
28
>>> sys.getsizeof("python")
55
>>> sys.getsizeof("java")
53
위 와 같이 정수 대상 이 사용 하 는 메모 리 는 모두 28 바이트 로 구체 적 인 값 과 상 관 없 이 똑 같이 문자열 대상 입 니 다.서로 다른 문자열 대상 이 사용 하 는 메모리 가 다 릅 니 다.이것 이 바로 길 어 지 는 대상 입 니 다.길 어 지 는 대상 에 대해 대상 이 정의 할 때 대상 이 사용 하 는 메모리 공간 이 얼마 인지 모 릅 니 다.
문자열 대상 은 Python 내부 에서 PyStringObject 로 PyStringObject 는 PyIntObject 와 마찬가지 로 가 변 대상 에 속 하 며 대상 이 생 성 되면 값 을 바 꿀 수 없습니다.(주의:길 어 지 는 대상 과 가 변 하지 않 는 대상 은 서로 다른 개념 입 니 다).Python StringObject 의 정의:

[stringobject.h]
typedef struct {
PyObject_VAR_HEAD
long ob_shash;
int ob_sstate;
char ob_sval[1];
} PyStringObject;
Python 의 문자열 대상 내 부 는 하나의 문자 배열 로 유지 되 고 있 음 을 알 수 있 습 니 다.정수 의 실현 원리 에 대해 서 는 PyObject 를 언급 합 니 다.HEAD,PyObject 에 대해VAR_HEAD 는 바로 PyObjectHEAD 에 ob 하나 더 추가크기 속성:

[object.h]
#define PyObject_VAR_HEAD  
 PyObject_HEAD   
 int ob_size; /* Number of items in variable part */
typedef struct {
 PyObject_VAR_HEAD
} PyVarObject;
  • ob_size 는 길 어 지 는 대상 에 있 는 요소 의 길 이 를 저장 합 니 다.예 를 들 어 PyStringObject 대상 인"Python"의 obsize 는 6..
  • ob_sval 은 초기 크기 가 1 인 문자 배열 이 고 obsval[0]='\0'이지 만 실제로 PyStringObject 를 만 들 때 obsval 이 가리 키 는 것 은 ob 입 니 다.size+1 바이트 의 메모리..
  • ob_shash 는 문자열 대상 의 해시 값 입 니 다.초기 값 은-1 입 니 다.문자열 의 해시 값 을 처음 계산 한 후 이 값 을 캐 시 하여 ob 에 할당 합 니 다.shash。
  • ob_sstate 는 이 문자열 의 대상 이 intern 메커니즘 에 들 어 갔 는 지 여 부 를 표시 하 는 데 사 용 됩 니 다
  • PYStringOBJECT 대상 생 성 과정
    
    [stringobject.c]
    PyObject * PyString_FromString(const char *str)
    {
    register size_t size;
    register PyStringObject *op;
    assert(str != NULL);
    size = strlen(str);
    // [1]
    if (size > PY_SSIZE_T_MAX - PyStringObject_SIZE) {
    PyErr_SetString(PyExc_OverflowError,
    "string is too long for a Python string");
    return NULL;
    }
    // [2]
    if (size == 0 && (op = nullstring) != NULL) {
    #ifdef COUNT_ALLOCS
    null_strings++;
    #endif
    Py_INCREF(op);
    return (PyObject *)op;
    }
    // [3]
    if (size == 1 && (op = characters[*str & UCHAR_MAX]) != NULL) {
    #ifdef COUNT_ALLOCS
    one_strings++;
    #endif
    Py_INCREF(op);
    return (PyObject *)op;
    }
    // [4]
    /* Inline PyObject_NewVar */
    op = (PyStringObject *)PyObject_MALLOC(PyStringObject_SIZE + size);
    if (op == NULL)
    return PyErr_NoMemory();
    PyObject_INIT_VAR(op, &PyString_Type, size);
    op->ob_shash = -1;
    op->ob_sstate = SSTATE_NOT_INTERNED;
    Py_MEMCPY(op->ob_sval, str, size+1);
    /* share short strings */
    if (size == 0) {
    PyObject *t = (PyObject *)op;
    PyString_InternInPlace(&t);
    op = (PyStringObject *)t;
    nullstring = op;
    Py_INCREF(op);
    } else if (size == 1) {
    PyObject *t = (PyObject *)op;
    PyString_InternInPlace(&t);
    op = (PyStringObject *)t;
    characters[*str & UCHAR_MAX] = op;
    Py_INCREF(op);
    }
    return (PyObject *) op;
    }
  • 문자열 의 길이 가 Python 이 받 아들 일 수 있 는 최대 길이(32 비트 플랫폼 은 2G)를 초과 하면 Null 로 돌아 갑 니 다
  • 빈 문자열 이 라면 특별한 PyStringObject,즉 nullstring 을 되 돌려 줍 니 다
  • 문자열 의 길이 가 1 이면 특수 PyStringObject,즉 onestring 을 되 돌려 줍 니 다
  • 다른 상황 에서 메모 리 를 분배 하고 PyStringObject 를 초기 화 하 며 매개 변수 str 의 문자 배열 을 PyStringObject 의 ob 로 복사 합 니 다.sval 이 가리 키 는 메모리 공간..
  • 문자열 의 INTERN 메커니즘
    PyStringObject 의 obsstate 속성 은 문자열 대상 이 intern 메커니즘 을 거 쳤 는 지,intern 처리 후의 문자열 을 표시 하 는 데 사 용 됩 니 다.예 를 들 어"Python"은 해석 기 가 실행 되 는 과정 에서 유일한 문자열 인"Python"에 대응 하 는 PyStringObject 대상 만 있 습 니 다.
    
    >>> a = "python"
    >>> b = "python"
    >>> a is b
    True
    위 에서 보 듯 이 a 를 만 들 때 시스템 은 먼저 새로운 PyStringObject 대상 을 만 든 다음 에 intern 메커니즘 으로 처리 합 니 다(PyStringInterninPlace)이 어 intern 메커니즘 으로 처 리 된 PyStringObject 대상 을 찾 습 니 다.이 문자열 에 대응 하 는 PyStringObject 가 존재 하 는 것 을 발견 하면 이 대상 으로 돌아 갑 니 다.그렇지 않 으 면 방금 만 든 PyStringObject 를 intern 시스템 에 추가 합 니 다.a 와 b 문자열 의 액면가 가 같 기 때문에 a 와 b 는 같은 PyStringObject("python")대상 을 가리킨다.그렇다면 인터넷 내 부 는 또 어떤 메커니즘 일 까?
    
    [stringobject.c]
    static PyObject *interned;
    void PyString_InternInPlace(PyObject **p)
    {
    register PyStringObject *s = (PyStringObject *)(*p);
    PyObject *t;
    if (s == NULL || !PyString_Check(s))
    Py_FatalError("PyString_InternInPlace: strings only please!");
    /* If it's a string subclass, we don't really know what putting
    it in the interned dict might do. */
    // [1]
    if (!PyString_CheckExact(s))
    return;
    // [2]
    if (PyString_CHECK_INTERNED(s))
    return;
    // [3]
    if (interned == NULL) {
    interned = PyDict_New();
    if (interned == NULL) {
    PyErr_Clear(); /* Don't leave an exception */
    return;
    }
    }
    t = PyDict_GetItem(interned, (PyObject *)s);
    if (t) {
    Py_INCREF(t);
    Py_DECREF(*p);
    *p = t;
    return;
    }
    if (PyDict_SetItem(interned, (PyObject *)s, (PyObject *)s) < 0) {
    PyErr_Clear();
    return;
    }
    /* The two references in interned are not counted by refcnt.
    The string deallocator will take care of this */
    Py_REFCNT(s) -= 2;
    PyString_CHECK_INTERNED(s) = SSTATE_INTERNED_MORTAL;
    }
    1.우선 형식 검사,intern 메커니즘 은 문자열 만 처리 합 니 다.
    2.이 PyStringObject 대상 이 이미 intern 메커니즘 처 리 를 한 적 이 있다 면 바로 되 돌려 줍 니 다.
    3.interned 는 사실 사전 대상 입 니 다.null 일 때 사전 대상 을 초기 화 합 니 다.그렇지 않 으 면 이 사전 에 key(PyObject*)s 의 value 가 존재 하 는 지 확인 합 니 다.존재 한다 면 이 대상 의 인용 수 를 1 로 추가 하고 임시로 만 든 대상 의 인용 수 를 1 로 줄 입 니 다.그렇지 않 으 면(PyObject*)s 를 key 와 value 로 interned 사전 에 추가 하 는 동시에 인용 수 를 2 로 줄 입 니 다.이 두 인용 수 를 2 로 줄 인 것 은 interned 사전 에 의 해 인용 되 었 기 때 문 입 니 다.그러나 이 두 인용 은 쓰레기 회수 의 판단 근거 가 되 지 않 습 니 다.그렇지 않 으 면 문자열 대상 은 쓰레기 회수 기 에 의 해 영원히 수집 되 지 않 습 니 다.

    상기 코드 에서 b 에 게"python"값 을 부여 한 후 시스템 에 PyStringObject 대상 을 몇 개 만 들 었 습 니까?정 답 은:2.b 를 만 들 때 임시 PyStringObject 가 사전 키 로 서 intened 에서 PyStringObject 대상 의 값 이"python"인지 찾 습 니 다.
    문자열 버퍼
    문자열 은 intern 메커니즘 캐 시 문자열 외 에 도 짧 은 문자열 버퍼 풀 characters 가 있 습 니 다.캐 시 문자열 길이 가 1 인 PyStringObject 대상 입 니 다.
    
    static PyStringObject *characters[UCHAR_MAX + 1]; //UCHAR_MAX = 255
    길이 가 1 인 문자열 을 만 들 때 흐름:
    
    ...
    else if (size == 1) {
    PyObject *t = (PyObject *)op;
    PyString_InternInPlace(&t);
    op = (PyStringObject *)t;
    characters[*str & UCHAR_MAX] = op;
    Py_INCREF(op);
  • 먼저 PyStringObject 대상 을 만 듭 니 다
  • 인터넷 조작 을 하 다.
  • PyStringObject 를 characters 에 캐 시 합 니 다
  • 인용 계수 증가 1
    요약:
    1.문자열 은 PyStringObject 로 표시
    2.문자열 은 길 어 지 는 대상 에 속 합 니 다.
    3.문자열 은 가 변 대상 에 속 합 니 다.
    4.문자열 은 intern 메커니즘 으로 python 의 효율 을 높 인 다.
    5.문자열 은 버퍼 저장 길이 가 1 인 문자열 대상 이 있 습 니 다.
    이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

    좋은 웹페이지 즐겨찾기