ไวยากรณ์ของสำนวนที่เตรียมไว้สำหรับรายการหลามแบบสั้นคืออะไร


543

list.append()เป็นตัวเลือกที่ชัดเจนสำหรับการเพิ่มในส่วนท้ายของรายการ นี่เป็นคำอธิบายที่เหมาะสมlist.prepend()สำหรับการขาดหายไป สมมติว่ารายการของฉันสั้นและกังวลเรื่องประสิทธิภาพเล็กน้อยคือ

list.insert(0, x)

หรือ

list[0:0] = [x]

สำนวน?

คำตอบ:


784

s.insert(0, x)รูปแบบที่พบมากที่สุด

เมื่อใดก็ตามที่คุณเห็นมันอาจถึงเวลาที่จะต้องพิจารณาใช้collection.dequeแทนรายการ


9
"เมื่อใดก็ตามที่คุณเห็นมันอาจถึงเวลาที่จะต้องพิจารณาการใช้ชุดสะสมแทนรายการ" ทำไมนี้
Matt M.

6
@MattM หากคุณใส่ที่ด้านหน้าของรายการหลามจะต้องย้ายรายการอื่นทั้งหมดไปข้างหน้าหนึ่งช่องรายการจะไม่สามารถ "ทำให้มีช่องว่างที่ด้านหน้า" collection.deque (คิวสิ้นสุดวันที่สองครั้ง) ได้รับการสนับสนุนสำหรับ "การสร้างพื้นที่ด้านหน้า" และเร็วกว่ามากในกรณีนี้
fejfo

265

หากคุณสามารถใช้งานได้สิ่งต่อไปนี้ค่อนข้างชัดเจน

new_list = [x] + your_list

แน่นอนว่าคุณยังไม่ได้แทรกxเข้ามาyour_listแต่คุณได้สร้างรายการใหม่ที่มีxคำนำไว้แล้ว


45
ในขณะที่คุณสังเกตเห็นว่าไม่ได้เป็นการเตรียมรายการ มันกำลังสร้างรายการใหม่ ดังนั้นจึงไม่ตอบคำถามเลย
Chris Morgan

112
แม้ว่ามันจะไม่ได้ตอบคำถาม แต่มันก็ปัดออกและนั่นคือจุดประสงค์ของเว็บไซต์นี้ ขอบคุณความคิดเห็นและคุณพูดถูก แต่เมื่อผู้คนค้นหาสิ่งนี้มันจะมีประโยชน์เมื่อเห็นสิ่งนี้
dave4jr

2
นอกจากนี้หากคุณต้องการเติมรายการลงในรายการการใช้ส่วนแทรกจะไม่ทำงานตามที่คาดไว้ แต่วิธีนี้จะ!
gota

89

ไวยากรณ์ของสำนวนที่เตรียมไว้สำหรับรายการหลามแบบสั้นคืออะไร

โดยปกติคุณไม่ต้องการที่จะเพิ่มรายการซ้ำใน Python ซ้ำ ๆ

ถ้ามันสั้นและคุณไม่ได้ทำมันมาก ... ก็โอเค

list.insert

list.insertสามารถใช้วิธีนี้

list.insert(0, x)

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

นี่คือตัวอย่างข้อมูลจากแหล่ง CPythonที่มีการนำไปใช้ - และอย่างที่คุณเห็นเราเริ่มต้นที่ส่วนท้ายของอาร์เรย์และย้ายทุกอย่างลงทีละรายการสำหรับการแทรกทุกครั้ง:

for (i = n; --i >= where; )
    items[i+1] = items[i];

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

deque.appendleft

A collections.dequeมีวิธีการมากมายในรายการ list.sortเป็นข้อยกเว้นทำให้dequeแตกหักไม่ได้ทั้งหมด Liskov listทดแทนสำหรับ

>>> set(dir(list)) - set(dir(deque))
{'sort'}

dequeนอกจากนี้ยังมีappendleftวิธีการ (เช่นเดียวกับpopleft) dequeเป็นคิวสองครั้งที่สิ้นสุดและรายการทวีคูณเชื่อมโยง - ไม่ว่ายาวก็มักจะใช้เวลาเท่ากันของเวลาที่จะมีอะไรบางอย่าง preprend ในสัญกรณ์ O ขนาดใหญ่ O (1) กับเวลา O (n) สำหรับรายการ นี่คือการใช้งาน:

>>> import collections
>>> d = collections.deque('1234')
>>> d
deque(['1', '2', '3', '4'])
>>> d.appendleft('0')
>>> d
deque(['0', '1', '2', '3', '4'])

deque.extendleft

ที่เกี่ยวข้องยังเป็นextendleftวิธีการของ deque ซึ่งซ้ำแล้วซ้ำอีกเตรียม:

>>> from collections import deque
>>> d2 = deque('def')
>>> d2.extendleft('cba')
>>> d2
deque(['a', 'b', 'c', 'd', 'e', 'f'])

โปรดทราบว่าแต่ละองค์ประกอบจะได้รับการเติมครั้งละหนึ่งรายการจึงจะกลับคำสั่งซื้อของพวกเขาได้อย่างมีประสิทธิภาพ

ประสิทธิภาพของlistเมื่อเทียบกับdeque

ก่อนอื่นเราตั้งค่าด้วยการเพิ่มซ้ำแบบซ้ำ ๆ :

import timeit
from collections import deque

def list_insert_0():
    l = []
    for i in range(20):
        l.insert(0, i)

def list_slice_insert():
    l = []
    for i in range(20):
        l[:0] = [i]      # semantically same as list.insert(0, i)

def list_add():
    l = []
    for i in range(20):
        l = [i] + l      # caveat: new list each time

def deque_appendleft():
    d = deque()
    for i in range(20):
        d.appendleft(i)  # semantically same as list.insert(0, i)

def deque_extendleft():
    d = deque()
    d.extendleft(range(20)) # semantically same as deque_appendleft above

และประสิทธิภาพ:

>>> min(timeit.repeat(list_insert_0))
2.8267281929729506
>>> min(timeit.repeat(list_slice_insert))
2.5210217320127413
>>> min(timeit.repeat(list_add))
2.0641671380144544
>>> min(timeit.repeat(deque_appendleft))
1.5863927800091915
>>> min(timeit.repeat(deque_extendleft))
0.5352169770048931

deque เร็วขึ้นมาก เมื่อรายการยาวขึ้นฉันคาดว่า deque จะทำงานได้ดีขึ้น หากคุณสามารถใช้ deque extendleftคุณจะได้รับประสิทธิภาพที่ดีที่สุด


57

หากมีคนพบคำถามนี้เหมือนฉันนี่คือการทดสอบประสิทธิภาพของฉันสำหรับวิธีการที่เสนอ:

Python 2.7.8

In [1]: %timeit ([1]*1000000).insert(0, 0)
100 loops, best of 3: 4.62 ms per loop

In [2]: %timeit ([1]*1000000)[0:0] = [0]
100 loops, best of 3: 4.55 ms per loop

In [3]: %timeit [0] + [1]*1000000
100 loops, best of 3: 8.04 ms per loop

อย่างที่คุณเห็นinsertและการแบ่งส่วนนั้นเร็วกว่าการเพิ่มอย่างชัดเจนเกือบสองเท่าและใกล้เคียงกับผลลัพธ์มาก ดังที่Raymond Hettingerตั้งข้อสังเกตว่าinsertเป็นตัวเลือกที่ใช้กันทั่วไปมากกว่าและโดยส่วนตัวแล้วผมชอบวิธีนี้ในการเพิ่มรายชื่อ


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

6
@ Dakkaron ฉันคิดว่าคุณผิดเกี่ยวกับเรื่องนี้ แหล่งที่มาค่อนข้างน้อยอ้างถึงความซับซ้อนเชิงเส้นสำหรับ list.insert เช่นตารางที่ดีนี้และโดยนัยตามคำอธิบายที่สมเหตุสมผลที่ผู้ถามเชื่อมโยง ฉันสงสัยว่า CPython จะจัดสรรแต่ละองค์ประกอบในหน่วยความจำในรายการในสองกรณีแรกดังนั้นทั้งสามรายการนี้อาจมีความซับซ้อนเชิงเส้น ฉันยังไม่ได้ดูรหัสหรือทดสอบด้วยตัวเอง แต่อย่างใดขออภัยด้วยถ้าแหล่งข้อมูลเหล่านั้นผิด Collections.deque.appendleft มีความซับซ้อนเชิงเส้นที่คุณกำลังพูดถึง
TC Proctor

@ Dakkaron ไม่เป็นความจริงสิ่งเหล่านี้มีความซับซ้อนเทียบเท่ากัน แม้ว่า.insertและ[0:0] = [0]ทำงานในสถานที่พวกเขายังคงต้องจัดสรรบัฟเฟอร์ทั้งหมดอีกครั้ง
juanpa.arrivillaga

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