Tuple dilimleme, dilimlemeyi listelemenin aksine yeni bir nesne döndürmüyor


12

Python'da (2 ve 3). Liste dilimlemeyi her kullandığımızda yeni bir nesne döndürür, örneğin:

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

Çıktı

>>> 140344378384464
>>> 140344378387272

Aynı şey demet ile tekrarlanırsa, aynı nesne döndürülür, örneğin:

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

Çıktı

>>> 140344379214896
>>> 140344379214896

Birisi bunun neden olduğuna ışık tutabilirse harika olurdu, Python deneyimim boyunca boş dilim yeni bir nesne döndürdüğü izlenimi altındaydım.

Anladığım kadarıyla, tuplelerin değişmez olduğu aynı nesneyi döndürmesi ve bunun yeni bir kopyasını oluşturmanın bir anlamı yok. Ama yine de, belgelerde hiçbir yerde bahsedilmiyor.



l2 = tuple(iter(l1))optimizasyonu atlar
Chris_Rands

Sorusunu gördükten sonra c-api'ninPyTuple_GetSlice yanlış belgelendiğini fark ettim . Dokümanlar şimdi düzeltildi (bu bpo sorunu 38557 idi ).
wim

Yanıtlar:


13

Uygulamalar değişmez türler için aynı örnekleri döndürmekte serbesttir (CPython'da bazen dizeler ve tamsayılar için benzer optimizasyonlar görebilirsiniz). Nesne değiştirilemediğinden, kullanıcı kodunda benzersiz bir örneği mi yoksa yalnızca varolan bir örneğe başka bir referansını mı tutturacağına dikkat etmesi gereken hiçbir şey yoktur.

Kısa devreyi burada C kodunda bulabilirsiniz .

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

Bu bir uygulama detayıdır, pypy'nin aynı şeyi yapmadığını unutmayın .


Teşekkürler @ wim. Bu şimdi mantıklı. C deneyimim yok gibi konu sadece bir şey a-> ob_item tam olarak ne yapar? Aramaya çalıştım. ama tüm anlayabildiğim "a" adresini alır ve "ob_item" ileri taşır. Benim anlayış ob_item "1" öğe yapan depolama adresi sayısını tutan oldu. #offTheTopic
Vijay Jangir

2
Bu, demet için typedef bakmak yardımcı olabilir burada . Yani a->ob_itemgibidir (*a).ob_itemyani denir üyesini alır, ob_itemgelen PyTupleObjectbir işaret ettiğini ve Ilow sonra dilim başlangıcına ilerletir +.
wim

3

Bu bir uygulama detayı. Listeler değiştirilebilir olduğundan, değişikliklerin etkilenmesini beklemediğiniz için bir kopya oluşturmanız l1[:] gerekir .l2l1

Bir demet değiştirilemez olduğundan , görünür herhangi bir şekilde t2etkileyecek hiçbir şey yapamazsınız t1, bu nedenle derleyici ve için aynı nesneyi kullanmakta serbesttir (ancak gerekli değildir ) .t1t1[:]


1

Python 3'te * my_list[:]sözdizimsel şekerdir. type(my_list).__getitem__(mylist, slice_object)Burada: 'ın özniteliklerinden (uzunluk) ve ifadesinden oluşturulan slice_objectbir dilim nesnesidir . Bu şekilde davranmasına Python veri modelindeki İndislenebilir dendiğini Nesneler bakın burada . Listeler ve tuples için yerleşik bir yöntemdir.my_list[:]__getitem__

CPython olarak, ve listeler ve tuplelar için, __getitem__baytkodu ameliyatla yorumlanır BINARY_SUBSCRtuplelar için uygulanan burada ve listeler için buraya .

Tuples durumunda, kod boyunca yürürken, bu kod bloğunda , öğe türdeyse ve dilimin tüm demet için değerlendirilirse , giriş bağımsız değişkeni ile static PyObject* tuplesubscript(PyTupleObject* self, PyObject* item)aynı referansı döndüreceğini görürsünüz .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. */
        ...
}

Şimdi kodu inceliyor static PyObject * list_subscript(PyListObject* self, PyObject* item)ve kendiniz görüyorsunuz, dilim ne olursa olsun, yeni bir liste nesnesinin her zaman döndürüldüğünü görüyorsunuz.


1
Bunun, yerleşik tipte bir dilimin dahil olmadığı bir dilimde geçmediği 2.7'de farklı olduğunu unutmayın . Bununla birlikte, genişletilmiş dilimleme abonelikten geçer. start:stoptup[:]BINARY_SUBSCRstart:stop:step
wim

Tamam, teşekkürler python sürümünü belirtmek için güncellenecek.
Fakher Mokadem

0

Bundan emin değilim ama görünüşe göre Python, aynı nesneye yeni bir işaretçi sağlıyor çünkü tupler aynı olduğundan (ve nesne bir demet olduğu için değişmez).

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.