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


12

파이썬 (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

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

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



l2 = tuple(iter(l1))최적화를 우회
Chris_Rands

주목 한 에 대한 C-APIPyTuple_GetSlice 질문을 본 후 부정확하게 기록되었다. 문서가 수정되었습니다 (이것은 bpo issue38557입니다 ).
wim

답변:


13

구현은 불변 유형에 대해 동일한 인스턴스를 자유롭게 리턴 할 수 있습니다 (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 는 동일하지 않습니다.


감사합니다 @wim. 이것은 이제 의미가 있습니다. 내가 C에서 경험하지 못했던 주제에서 한 가지만. a-> ob_item은 정확히 무엇을합니까? 나는 그것을 찾아 보았습니다. 그러나 내가 이해할 수있는 것은 "a"의 주소를 가져 와서 "ob_item"을 앞으로 이동시키는 것입니다. 내 이해는 ob_item은 "1"항목을 만드는 저장 주소 수를 보유하고 있다는 것입니다. #offTheTopic
Vijay Jangir

2
여기 tuple에 대한 typedef를 보는 것이 도움이 될 수 있습니다 . 그래서 a->ob_item같다 (*a).ob_item즉 그것이라고 회원 얻는다 ob_item으로부터 PyTupleObjectA가 가리키고 있는지를하고는 ilow 다음 조각의 시작으로 진행 +.
wim

3

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

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


1

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)슬라이스가 무엇이든 항상 새 목록 객체가 반환되는지 직접 확인하십시오.


1
을 포함하여 내장 유형 의 슬라이스 가 통과하지 않는 2.7 에서는 이 점이 다릅니다 . 그러나 확장 슬라이싱 은 구독을 거치게됩니다. start:stoptup[:]BINARY_SUBSCRstart:stop:step
wim

좋아, 파이썬 버전을 지정하도록 업데이트 할 것입니다.
Fakher Mokadem

0

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

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