Tuple slicing ไม่ส่งคืนวัตถุใหม่ซึ่งต่างจากการแบ่งส่วนรายการ


12

ใน 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

มันจะดีถ้ามีใครบางคนสามารถทำให้เข้าใจว่าทำไมสิ่งนี้ถึงเกิดขึ้นตลอดประสบการณ์ Python ของฉันฉันอยู่ภายใต้การแสดงผลชิ้นส่วนที่ว่างเปล่าส่งคืนวัตถุใหม่

ความเข้าใจของฉันคือการส่งคืนวัตถุเดียวกันกับสิ่งอันดับที่ไม่เปลี่ยนรูปและไม่มีจุดสร้างสำเนาใหม่ แต่ก็ไม่ได้กล่าวถึงในเอกสารทุกที่



l2 = tuple(iter(l1))ข้ามการปรับให้เหมาะสม
Chris_Rands

สังเกตเห็นc-api สำหรับPyTuple_GetSliceได้รับเอกสารไม่ถูกต้องหลังจากเห็นคำถามของคุณ ขณะนี้เอกสารได้รับการแก้ไขแล้ว (นี่คือปัญหาbpo 38557 )
Wim

คำตอบ:


13

การใช้งานมีอิสระที่จะส่งกลับอินสแตนซ์ที่เหมือนกันสำหรับประเภทที่ไม่เปลี่ยน (ใน CPython บางครั้งคุณอาจเห็นการปรับให้เหมาะสมสำหรับสตริงและจำนวนเต็ม) เนื่องจากไม่สามารถเปลี่ยนแปลงวัตถุได้จึงไม่มีอะไรในรหัสผู้ใช้ที่ต้องดูแลไม่ว่าจะมีอินสแตนซ์ที่ไม่ซ้ำกันหรือเพียงแค่การอ้างอิงอื่นไปยังอินสแตนซ์ที่มีอยู่

คุณสามารถค้นหาลัดวงจรในรหัสซีที่นี่

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
มันอาจจะช่วยไปดูที่ typedef สำหรับ tuple, ที่นี่ ดังนั้นa->ob_itemเป็นเหมือน(*a).ob_itemเช่นที่จะได้รับสมาชิกที่เรียกว่าob_itemจากPyTupleObjectที่จะชี้ไปและ + ilow แล้วก้าวหน้าจุดเริ่มต้นของชิ้นนั้น
Wim

3

มันเป็นรายละเอียดการใช้งาน เนื่องจากรายการไม่แน่นอนl1[:] ต้องสร้างสำเนาเพราะคุณไม่คาดว่าการเปลี่ยนแปลงl2จะส่งผลกระทบl1ส่งผลกระทบต่อ

ตั้งแต่ tuple คือไม่เปลี่ยนรูปแต่ไม่มีอะไรที่คุณสามารถทำได้เพื่อเป็นt2ที่จะส่งผลกระทบt1ในทางที่มองเห็นใด ๆ เพื่อให้คอมไพเลอร์ที่เป็นอิสระ ( แต่ไม่จำเป็น ) การใช้วัตถุเดียวกันสำหรับและt1t1[:]


1

ในหลาม 3. * my_list[:]เป็นน้ำตาลประโยคสำหรับtype(my_list).__getitem__(mylist, slice_object)ที่: slice_objectเป็นวัตถุชิ้นที่สร้างขึ้นจากmy_listคุณลักษณะ 's (ความยาว) [:]และการแสดงออก วัตถุที่ทำงานในลักษณะนี้จะเรียกว่า subscriptable ในรูปแบบข้อมูลหลามดูที่นี่ สำหรับรายการและสิ่งอันดับ__getitem__เป็นวิธีการในตัว

ใน CPython และสำหรับรายการและสิ่งอันดับ__getitem__ถูกตีความโดยการดำเนินการ bytecode BINARY_SUBSCRซึ่งถูกนำมาใช้สำหรับสิ่งอันดับในที่นี้และสำหรับรายการที่นี่ที่นี่

ในกรณีของ tuples เดินรหัสผ่านที่คุณจะเห็นว่าในการป้องกันรหัสนี้ , static PyObject* tuplesubscript(PyTupleObject* self, PyObject* item)จะกลับอ้างอิงถึงเหมือนกันPyTupleObjectว่ามันมีเป็นอาร์กิวเมนต์อินพุตถ้ารายการเป็นประเภทPySliceและประเมินชิ้นไป tuple ทั้งหมด

    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:stopชิ้นบนในตัวชนิดรวมทั้งไม่ได้ไปผ่านtup[:] BINARY_SUBSCRแม้ว่าการแบ่งส่วนเพิ่มเติมstart:stop:stepจะต้องผ่านการสมัครสมาชิก
Wim

โอเคขอบคุณที่จะอัพเดทเพื่อระบุเวอร์ชั่นของงูหลาม
Fakher Mokadem

0

ไม่แน่ใจเกี่ยวกับเรื่องนี้ แต่ดูเหมือนว่า Python จะให้ตัวชี้ใหม่ไปยังวัตถุเดียวกันเพื่อหลีกเลี่ยงการคัดลอกเนื่องจาก tuples เหมือนกัน (และเนื่องจากวัตถุนั้นเป็น tuple จึงไม่เปลี่ยนรูป)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.