[python] 목록 슬라이싱이 아닌 튜플 슬라이싱이 새 객체를 반환하지 않음

파이썬 (2와 3). 리스트 슬라이싱을 사용할 때마다 새로운 객체를 반환합니다.

l1 = [1,2,3,4]
print(id(l1))
l2 = l1[:]
print(id(l2))

산출

>>> 140344378384464
>>> 140344378387272

같은 것을 tuple로 반복하면 같은 객체가 반환됩니다. 예 :

t1 = (1,2,3,4)
t2 = t1[:]
print(id(t1))
print(id(t2))

산출

>>> 140344379214896
>>> 140344379214896

누군가가 왜 이런 일이 일어나고 있는지에 대해 밝힐 수 있다면, 파이썬 경험을 통해 빈 슬라이스가 새로운 객체를 반환한다는 인상을 받았습니다.

내 이해는 튜플이 변경 불가능한 것과 동일한 객체를 반환하고 새로운 사본을 만들 필요가 없다는 것입니다. 그러나 다시, 그것은 문서의 어느 곳에서도 언급되지 않았습니다.



답변

구현은 불변 유형에 대해 동일한 인스턴스를 자유롭게 리턴 할 수 있습니다 (CPython에서는 때때로 문자열 및 정수에 대해 유사한 최적화가 표시 될 수 있음). 객체를 변경할 수 없으므로 사용자 코드에는 객체가 고유 한 인스턴스를 보유하는지 또는 기존 인스턴스에 대한 또 다른 참조를 보유해야하는지 신경 쓸 필요가 없습니다.

당신은 C 코드에서 단락을 찾을 수 있습니다 여기에 .

static PyObject*
tuplesubscript(PyTupleObject* self, PyObject* item)
{
    ... /* note: irrelevant parts snipped out */
    if (start == 0 && step == 1 &&
                 slicelength == PyTuple_GET_SIZE(self) &&
                 PyTuple_CheckExact(self)) {
            Py_INCREF(self);          /* <--- increase reference count */
            return (PyObject *)self;  /* <--- return another pointer to same */
        }
    ...

이것은 구현 세부 사항입니다. pypy 는 동일하지 않습니다.


답변

구현 세부 사항입니다. 목록을 변경할 수 있기 때문에, l1[:] 한다 당신이 변화를 기대하지 않기 때문에, 복사본을 생성 l2에 영향을 l1.

튜플은 변경할 수 없으므로 눈에 보이는 방식으로 t2영향을 줄 t1수있는 방법이 없으므로 컴파일러는 및에 대해 동일한 객체를 사용할 수 있습니다 ( 필수는 아님) .t1t1[:]


답변

Python 3에서 * my_list[:]는 구문 설탕입니다. type(my_list).__getitem__(mylist, slice_object)여기서 : slice_objectmy_list속성 (길이)과 표현식으로 구성된 슬라이스 객체 [:]입니다. 이런 방식으로 동작하는 객체는 파이썬 데이터 모델에서 아래 첨자 가능이라고 합니다 . 여기를 참조 하십시오 . 목록과 튜플의 __getitem__경우 기본 제공 방법입니다.

CPython에서,리스트와 튜플 에 대해서는 여기의 튜플 과 여기 에있는리스트 대해 구현 된 __getitem__바이트 코드 연산으로 해석됩니다 .BINARY_SUBSCR

튜플의 경우 코드를 살펴보면 이 코드 블록 에서 item이 유형 이고 슬라이스가 전체 튜플로 평가되는 경우 입력 인수 static PyObject*
tuplesubscript(PyTupleObject* self, PyObject* item)
와 동일한 참조를 반환합니다 .PyTupleObjectPySlice

    static PyObject*
    tuplesubscript(PyTupleObject* self, PyObject* item)
    {
        /* checks if item is an index */
        if (PyIndex_Check(item)) {
            ...
        }
        /* else it is a slice */
        else if (PySlice_Check(item)) {
            ...
        /* unpacks the slice into start, stop and step */
        if (PySlice_Unpack(item, &start, &stop, &step) < 0) {
            return NULL;
        }
       ...
        }
        /* if we start at 0, step by 1 and end by the end of the tuple then !! look down */
        else if (start == 0 && step == 1 &&
                 slicelength == PyTuple_GET_SIZE(self) &&
                 PyTuple_CheckExact(self)) {
            Py_INCREF(self); /* increase the reference count for the tuple */
            return (PyObject *)self; /* and return a reference to the same tuple. */
        ...
}

이제 코드를 검사하고 static PyObject *
list_subscript(PyListObject* self, PyObject* item)
슬라이스가 무엇이든 항상 새 목록 객체가 반환되는지 직접 확인하십시오.


답변

확실하지 않지만 파이썬은 튜플이 동일하기 때문에 복사를 피하기 위해 동일한 객체에 대한 새로운 포인터를 제공하는 것으로 보입니다 (객체는 튜플이므로 불변입니다).


답변