python - Tuple slicing non restituisce un nuovo oggetto invece di list slicing




tuples slice (3)

In Python (2 e 3). Ogni volta che utilizziamo la suddivisione in elenchi restituisce un nuovo oggetto, ad esempio:

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

Produzione

>>> 140344378384464
>>> 140344378387272

Se la stessa cosa si ripete con la tupla, viene restituito lo stesso oggetto, ad es .:

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

Produzione

>>> 140344379214896
>>> 140344379214896

Sarebbe bello se qualcuno potesse far luce sul perché ciò sta accadendo, durante la mia esperienza con Python ero sotto l'impressione che la fetta vuota restituisse un nuovo oggetto.

La mia comprensione è che sta restituendo lo stesso oggetto in quanto le tuple sono immutabili e non ha senso crearne una nuova copia. Ma ancora una volta, non è menzionato nei documenti da nessuna parte.


È un dettaglio di implementazione. Poiché gli elenchi sono modificabili, l1[:] deve creare una copia, poiché non ci si aspetterebbe che le modifiche a l2 influiscano su l1 .

Dato che una tupla è immutabile , tuttavia, non c'è nulla che tu possa fare per t2 che possa influenzare t1 in alcun modo visibile, quindi il compilatore è libero (ma non necessario ) di usare lo stesso oggetto per t1 e t1[:] .


In Python 3. * my_list[:] è lo zucchero sintattico per type(my_list).__getitem__(mylist, slice_object) dove: slice_object è un oggetto slice creato dagli my_list di my_list (lunghezza) e dall'espressione [:] . Gli oggetti che si comportano in questo modo sono chiamati sottoscrivibili nel modello di dati Python, vedere here . Per elenchi e tuple __getitem__ è un metodo integrato.

In CPython, e per elenchi e tuple, __getitem__ viene interpretato dall'operazione bytecode BINARY_SUBSCR che è implementata per le tuple qui e per gli elenchi qui .

In caso di tuple, static PyObject* tuplesubscript(PyTupleObject* self, PyObject* item) il codice vedrai che in questo blocco di codice , static PyObject* tuplesubscript(PyTupleObject* self, PyObject* item) restituirà un riferimento allo stesso PyTupleObject che ha ottenuto come argomento di input, se l'elemento è di digitare PySlice e la sezione PySlice l'intera tupla.

    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. */
        ...
}

Ora si esamina il codice per static PyObject * list_subscript(PyListObject* self, PyObject* item) e si vede da sé che qualunque sia la sezione, viene sempre restituito un nuovo oggetto elenco.


Non ne sono sicuro, ma sembra che Python fornisca un nuovo puntatore allo stesso oggetto per evitare la copia poiché le tuple sono identiche (e poiché l'oggetto è una tupla, è immutabile).







cpython