Python - สร้างรายการที่มีความจุเริ่มต้น


188

รหัสเช่นนี้มักจะเกิดขึ้น:

l = []
while foo:
    #baz
    l.append(bar)
    #qux

สิ่งนี้ช้ามากหากคุณกำลังจะผนวกองค์ประกอบหลายพันรายการไว้ในรายการของคุณเนื่องจากรายการจะต้องมีการปรับขนาดอย่างต่อเนื่องเพื่อให้พอดีกับองค์ประกอบใหม่

ใน Java คุณสามารถสร้าง ArrayList ด้วยความจุเริ่มต้น หากคุณมีความคิดว่ารายการของคุณจะใหญ่ขนาดไหนมันจะมีประสิทธิภาพมากขึ้น

ฉันเข้าใจว่ารหัสเช่นนี้มักจะถูกนำมารวมเข้ากับความเข้าใจในรายการอีกครั้ง ถ้าวนลูป for / while มีความซับซ้อนมากสิ่งนี้ไม่สามารถทำได้ มีอะไรที่เทียบเท่ากับโปรแกรมเมอร์ Python หรือไม่?


12
เท่าที่ฉันรู้พวกเขาก็คล้ายกับ ArrayLists ที่พวกเขาเพิ่มขนาดของพวกเขาเป็นสองเท่าในแต่ละครั้ง เวลาตัดจำหน่ายของการดำเนินการนี้เป็นค่าคงที่ มันไม่ได้เป็นผลงานที่ยิ่งใหญ่อย่างที่คุณคิด
mmcdole

ดูเหมือนว่าคุณจะถูก!
Claudiu

11
บางทีการเตรียมใช้งานล่วงหน้านั้นไม่จำเป็นสำหรับสถานการณ์ของ OP แต่บางครั้งก็จำเป็นต้องใช้อย่างแน่นอน: ฉันมีรายการที่มีการทำดัชนีไว้ล่วงหน้าจำนวนหนึ่งซึ่งต้องถูกแทรกลงในดัชนีที่ระบุ ฉันจำเป็นต้องเพิ่มรายชื่อล่วงหน้าเพื่อหลีกเลี่ยงการใช้ IndexErrors ขอบคุณสำหรับคำถามนี้
Neil Traft

1
@Claudiu คำตอบที่ยอมรับนั้นทำให้เข้าใจผิด ความคิดเห็นที่ได้รับการโหวตสูงสุดภายใต้อธิบายว่าทำไม คุณจะพิจารณายอมรับคำตอบอย่างใดอย่างหนึ่งหรือไม่
Neal Gokli

คำตอบ:


126
def doAppend( size=10000 ):
    result = []
    for i in range(size):
        message= "some unique object %d" % ( i, )
        result.append(message)
    return result

def doAllocate( size=10000 ):
    result=size*[None]
    for i in range(size):
        message= "some unique object %d" % ( i, )
        result[i]= message
    return result

ผล (ประเมินแต่ละฟังก์ชั่น 144 ครั้งและระยะเวลาเฉลี่ย)

simple append 0.0102
pre-allocate  0.0098

ข้อสรุป มันแทบจะไม่สำคัญ

การเพิ่มประสิทธิภาพก่อนวัยอันควรเป็นรากของความชั่วร้ายทั้งหมด


18
จะเกิดอะไรขึ้นถ้าวิธีการจัดสรรล่วงหน้า (ขนาด * [ไม่มี]) นั้นไม่มีประสิทธิภาพ? python VM จัดสรรรายชื่อพร้อมกันหรือค่อย ๆ เพิ่มขึ้นเช่นเดียวกับภาคผนวก () หรือไม่
haridsv

9
เฮ้ มันน่าจะสามารถแสดงใน Python ได้ แต่ก็ยังไม่มีใครโพสต์ไว้ที่นี่ จุดของ haridsv คือเราแค่สมมติว่า 'รายการ int *' ไม่เพียง แต่ผนวกเข้ากับรายการตามรายการ สมมติฐานนั้นอาจถูกต้อง แต่ประเด็นของ haridsv ก็คือเราควรตรวจสอบสิ่งนั้น ถ้ามันไม่ถูกต้องนั่นจะอธิบายว่าทำไมทั้งสองฟังก์ชั่นที่คุณแสดงให้เห็นนั้นเกือบจะเท่ากันเพราะภายใต้ฝาปิดพวกเขากำลังทำสิ่งเดียวกันดังนั้นจึงยังไม่ได้ทดสอบหัวข้อของคำถามนี้ ขอแสดงความนับถืออย่างสูง!
Jonathan Hartley

136
สิ่งนี้ไม่ถูกต้อง คุณกำลังจัดรูปแบบสตริงด้วยการวนซ้ำแต่ละครั้งซึ่งจะสัมพันธ์กับสิ่งที่คุณพยายามทดสอบตลอดไป นอกจากนี้ให้ที่ 4% ยังคงสามารถมีความสำคัญขึ้นอยู่กับสถานการณ์และก็ประมาท ...
ฟิลิป Guin

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

12
คำตอบที่ไม่ถูกต้องพร้อมกับ upvotes จำนวนมากยังเป็นรากฐานของความชั่วร้ายทั้งหมด
ฮาชิโมโตะ

80

รายการ Python ไม่มีการจัดสรรล่วงหน้าในตัว หากคุณต้องการทำรายการจริงๆและต้องการหลีกเลี่ยงค่าใช้จ่ายในการต่อท้าย (และคุณควรยืนยันว่าคุณทำ) คุณสามารถทำสิ่งนี้ได้:

l = [None] * 1000 # Make a list of 1000 None's
for i in xrange(1000):
    # baz
    l[i] = bar
    # qux

บางทีคุณสามารถหลีกเลี่ยงรายการโดยใช้ตัวสร้างแทน:

def my_things():
    while foo:
        #baz
        yield bar
        #qux

for thing in my_things():
    # do something with thing

ด้วยวิธีนี้รายการไม่ได้ถูกจัดเก็บไว้ในหน่วยความจำ แต่เพียงสร้างตามต้องการ


7
+1 Generators แทนที่จะเป็นรายการ อัลกอริทึมจำนวนมากสามารถแก้ไขได้เล็กน้อยเพื่อให้ทำงานกับเครื่องกำเนิดไฟฟ้าแทนรายการที่เป็นรูปธรรมเต็ม
S.Lott

เครื่องกำเนิดไฟฟ้าเป็นความคิดที่ดีจริง ฉันต้องการวิธีการทั่วไปนอกเหนือไปจากที่ตั้งไว้ ฉันคิดว่าความแตกต่างนั้นเล็กน้อย thoguh
Claudiu

50

เวอร์ชั่นสั้น: ใช้

pre_allocated_list = [None] * size

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

รุ่นยาว:

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

สำหรับ Python 3.2:

import time
import copy

def print_timing (func):
  def wrapper (*arg):
    t1 = time.time ()
    res = func (*arg)
    t2 = time.time ()
    print ("{} took {} ms".format (func.__name__, (t2 - t1) * 1000.0))
    return res

  return wrapper

@print_timing
def prealloc_array (size, init = None, cp = True, cpmethod=copy.deepcopy, cpargs=(), use_num = False):
  result = [None] * size
  if init is not None:
    if cp:
      for i in range (size):
          result[i] = init
    else:
      if use_num:
        for i in range (size):
            result[i] = cpmethod (i)
      else:
        for i in range (size):
            result[i] = cpmethod (cpargs)
  return result

@print_timing
def prealloc_array_by_appending (size):
  result = []
  for i in range (size):
    result.append (None)
  return result

@print_timing
def prealloc_array_by_extending (size):
  result = []
  none_list = [None]
  for i in range (size):
    result.extend (none_list)
  return result

def main ():
  n = 1000000
  x = prealloc_array_by_appending(n)
  y = prealloc_array_by_extending(n)
  a = prealloc_array(n, None)
  b = prealloc_array(n, "content", True)
  c = prealloc_array(n, "content", False, "some object {}".format, ("blah"), False)
  d = prealloc_array(n, "content", False, "some object {}".format, None, True)
  e = prealloc_array(n, "content", False, copy.deepcopy, "a", False)
  f = prealloc_array(n, "content", False, copy.deepcopy, (), False)
  g = prealloc_array(n, "content", False, copy.deepcopy, [], False)

  print ("x[5] = {}".format (x[5]))
  print ("y[5] = {}".format (y[5]))
  print ("a[5] = {}".format (a[5]))
  print ("b[5] = {}".format (b[5]))
  print ("c[5] = {}".format (c[5]))
  print ("d[5] = {}".format (d[5]))
  print ("e[5] = {}".format (e[5]))
  print ("f[5] = {}".format (f[5]))
  print ("g[5] = {}".format (g[5]))

if __name__ == '__main__':
  main()

การประเมินผล:

prealloc_array_by_appending took 118.00003051757812 ms
prealloc_array_by_extending took 102.99992561340332 ms
prealloc_array took 3.000020980834961 ms
prealloc_array took 49.00002479553223 ms
prealloc_array took 316.9999122619629 ms
prealloc_array took 473.00004959106445 ms
prealloc_array took 1677.9999732971191 ms
prealloc_array took 2729.999780654907 ms
prealloc_array took 3001.999855041504 ms
x[5] = None
y[5] = None
a[5] = None
b[5] = content
c[5] = some object blah
d[5] = some object 5
e[5] = a
f[5] = []
g[5] = ()

อย่างที่คุณเห็นการสร้างรายการอ้างอิงขนาดใหญ่ไปยังวัตถุไม่มีตัวเดียวกันนั้นใช้เวลาน้อยมาก

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

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


อืมน่าสนใจ ดังนั้นไรคำตอบคือ - มันไม่สำคัญว่าคุณจะทำการใด ๆ เพื่อใส่องค์ประกอบในรายการ แต่ถ้าคุณแค่ต้องการรายการใหญ่ขององค์ประกอบเดียวกันทั้งหมดคุณควรใช้[]*วิธีนี้
Claudiu

26

วิธี Pythonic สำหรับสิ่งนี้คือ:

x = [None] * numElements

หรือค่าเริ่มต้นใด ๆ ที่คุณต้องการจะเติมด้วยเช่น

bottles = [Beer()] * 99
sea = [Fish()] * many
vegetarianPizzas = [None] * peopleOrderingPizzaNotQuiche

[แก้ไข: ข้อแม้ Emptor[Beer()] * 99ไวยากรณ์สร้างหนึ่ง Beerแล้ว populates อาร์เรย์ 99 อ้างอิงถึงเช่นเดียวเดียวกัน]

วิธีการเริ่มต้นของ Python นั้นค่อนข้างมีประสิทธิภาพแม้ว่าประสิทธิภาพนั้นจะลดลงเมื่อคุณเพิ่มจำนวนองค์ประกอบ

เปรียบเทียบ

import time

class Timer(object):
    def __enter__(self):
        self.start = time.time()
        return self

    def __exit__(self, *args):
        end = time.time()
        secs = end - self.start
        msecs = secs * 1000  # millisecs
        print('%fms' % msecs)

Elements   = 100000
Iterations = 144

print('Elements: %d, Iterations: %d' % (Elements, Iterations))


def doAppend():
    result = []
    i = 0
    while i < Elements:
        result.append(i)
        i += 1

def doAllocate():
    result = [None] * Elements
    i = 0
    while i < Elements:
        result[i] = i
        i += 1

def doGenerator():
    return list(i for i in range(Elements))


def test(name, fn):
    print("%s: " % name, end="")
    with Timer() as t:
        x = 0
        while x < Iterations:
            fn()
            x += 1


test('doAppend', doAppend)
test('doAllocate', doAllocate)
test('doGenerator', doGenerator)

กับ

#include <vector>
typedef std::vector<unsigned int> Vec;

static const unsigned int Elements = 100000;
static const unsigned int Iterations = 144;

void doAppend()
{
    Vec v;
    for (unsigned int i = 0; i < Elements; ++i) {
        v.push_back(i);
    }
}

void doReserve()
{
    Vec v;
    v.reserve(Elements);
    for (unsigned int i = 0; i < Elements; ++i) {
        v.push_back(i);
    }
}

void doAllocate()
{
    Vec v;
    v.resize(Elements);
    for (unsigned int i = 0; i < Elements; ++i) {
        v[i] = i;
    }
}

#include <iostream>
#include <chrono>
using namespace std;

void test(const char* name, void(*fn)(void))
{
    cout << name << ": ";

    auto start = chrono::high_resolution_clock::now();
    for (unsigned int i = 0; i < Iterations; ++i) {
        fn();
    }
    auto end = chrono::high_resolution_clock::now();

    auto elapsed = end - start;
    cout << chrono::duration<double, milli>(elapsed).count() << "ms\n";
}

int main()
{
    cout << "Elements: " << Elements << ", Iterations: " << Iterations << '\n';

    test("doAppend", doAppend);
    test("doReserve", doReserve);
    test("doAllocate", doAllocate);
}

ใน Windows 7 i7 ของฉัน Python 64 บิตให้

Elements: 100000, Iterations: 144
doAppend: 3587.204933ms
doAllocate: 2701.154947ms
doGenerator: 1721.098185ms

ขณะที่ C ++ ให้ (สร้างด้วย MSVC, 64- บิต, เปิดใช้งานการปรับให้เหมาะสมที่สุด)

Elements: 100000, Iterations: 144
doAppend: 74.0042ms
doReserve: 27.0015ms
doAllocate: 5.0003ms

สร้างการดีบัก C ++:

Elements: 100000, Iterations: 144
doAppend: 2166.12ms
doReserve: 2082.12ms
doAllocate: 273.016ms

ประเด็นก็คือเมื่อใช้ Python คุณจะสามารถปรับปรุงประสิทธิภาพได้ 7-8% และถ้าคุณคิดว่าคุณกำลังเขียนแอพที่มีประสิทธิภาพสูง (หรือถ้าคุณกำลังเขียนสิ่งที่ใช้ในบริการเว็บหรืออะไรก็ตาม) ที่ไม่ควรสูดดม แต่คุณอาจต้องคิดใหม่เกี่ยวกับภาษาที่คุณเลือก

นอกจากนี้รหัส Python ที่นี่ไม่ใช่รหัส Python จริงๆ การเปลี่ยนเป็นรหัส Pythonesque อย่างแท้จริงให้ประสิทธิภาพที่ดีกว่า:

import time

class Timer(object):
    def __enter__(self):
        self.start = time.time()
        return self

    def __exit__(self, *args):
        end = time.time()
        secs = end - self.start
        msecs = secs * 1000  # millisecs
        print('%fms' % msecs)

Elements   = 100000
Iterations = 144

print('Elements: %d, Iterations: %d' % (Elements, Iterations))


def doAppend():
    for x in range(Iterations):
        result = []
        for i in range(Elements):
            result.append(i)

def doAllocate():
    for x in range(Iterations):
        result = [None] * Elements
        for i in range(Elements):
            result[i] = i

def doGenerator():
    for x in range(Iterations):
        result = list(i for i in range(Elements))


def test(name, fn):
    print("%s: " % name, end="")
    with Timer() as t:
        fn()


test('doAppend', doAppend)
test('doAllocate', doAllocate)
test('doGenerator', doGenerator)

ซึ่งจะช่วยให้

Elements: 100000, Iterations: 144
doAppend: 2153.122902ms
doAllocate: 1346.076965ms
doGenerator: 1614.092112ms

(ใน doGenerator แบบ 32 บิตทำได้ดีกว่า doAllocate)

ที่นี่ช่องว่างระหว่าง doAppend และ doAllocate มีขนาดใหญ่กว่าอย่างมาก

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

จุดที่นี่: ทำมันด้วยวิธีไพทอนเพื่อประสิทธิภาพที่ดีที่สุด

แต่ถ้าคุณกังวลเรื่องประสิทธิภาพทั่วไปในระดับสูง Python เป็นภาษาที่ผิด ปัญหาพื้นฐานที่สุดคือการเรียกใช้ฟังก์ชั่นของ Python นั้นช้ากว่าภาษาอื่นถึง 300x เนื่องจากคุณสมบัติของ Python เช่น decorators etc ( https://wiki.python.org/moin/PythonSpeed/PerformanceTips#Data_Aggregation#Data_Aggregation )


@NilsvonBarth C ++ ไม่มีtimeit
kfsone

Pythonมีtimeitซึ่งคุณควรใช้เมื่อกำหนดเวลารหัส Python ของคุณ ฉันไม่ได้พูดถึง C ++ แน่นอน
Nils von Barth

4
นี่ไม่ใช่คำตอบที่ถูกต้อง bottles = [Beer()] * 99ไม่สร้างวัตถุเบียร์ 99 อัน สร้างวัตถุเบียร์หนึ่งชิ้นโดยอ้างอิง 99 รายการแทน หากคุณต้องการเปลี่ยนแปลงมันองค์ประกอบทั้งหมดในรายการจะมีการกลายพันธุ์ทำให้ทุก(bottles[i] is bootles[j]) == True i != j. 0<= i, j <= 99
erhesto

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

@YongweiWu คุณพูดถูกจริงๆ ตัวอย่างนี้ไม่ได้ทำให้คำตอบทั้งหมดไม่ถูกต้องมันอาจจะทำให้เข้าใจผิดและมันก็คุ้มค่าที่จะพูดถึง
erhesto

8

ดังที่คนอื่น ๆ พูดถึงวิธีที่ง่ายที่สุดในการเตรียมรายการของNoneTypeวัตถุไว้ล่วงหน้า

ดังที่ได้กล่าวไปแล้วคุณควรเข้าใจวิธีการที่รายการ Python ใช้งานได้จริงก่อนที่จะตัดสินใจเลือกสิ่งนี้ ในการใช้งาน CPython ของรายการอาร์เรย์พื้นฐานจะถูกสร้างขึ้นพร้อมกับห้องที่มีขนาดใหญ่ขึ้นเรื่อย ๆ ในขนาดที่ใหญ่ขึ้นเรื่อย( 4, 8, 16, 25, 35, 46, 58, 72, 88, 106, 126, 148, 173, 201, 233, 269, 309, 354, 405, 462, 526, 598, 679, 771, 874, 990, 1120, etc)ๆ ดังนั้นการปรับขนาดรายการจะไม่เกิดขึ้นบ่อยนัก

เพราะพฤติกรรมนี้มากที่สุด list.append()ฟังก์ชั่นที่มีความซับซ้อนสำหรับการผนวกเพียงมีความซับซ้อนเพิ่มขึ้นเมื่อข้ามหนึ่งในขอบเขตเหล่านี้จุดที่ซับซ้อนจะเป็นO(1) O(n)พฤติกรรมนี้เป็นสิ่งที่นำไปสู่การเพิ่มขึ้นเล็กน้อยในเวลาดำเนินการในคำตอบของ S. Lott

ที่มา: http://www.laurentluce.com/posts/python-list-implementation/


4

ฉันรันโค้ดของ @ s.lott และสร้างการเพิ่มขึ้น 10% เหมือนกันโดยการจัดสรรล่วงหน้า ลองใช้ความคิดของ @ jeremy โดยใช้เครื่องกำเนิดไฟฟ้าและสามารถเห็นความสมบูรณ์แบบของยีนได้ดีกว่าของ doAllocate สำหรับโครงการของฉันมีการปรับปรุง 10% ดังนั้นขอขอบคุณทุกคนเพราะนี่เป็นประโยชน์

def doAppend( size=10000 ):
    result = []
    for i in range(size):
        message= "some unique object %d" % ( i, )
        result.append(message)
    return result

def doAllocate( size=10000 ):
    result=size*[None]
    for i in range(size):
        message= "some unique object %d" % ( i, )
        result[i]= message
    return result

def doGen( size=10000 ):
    return list("some unique object %d" % ( i, ) for i in xrange(size))

size=1000
@print_timing
def testAppend():
    for i in xrange(size):
        doAppend()

@print_timing
def testAlloc():
    for i in xrange(size):
        doAllocate()

@print_timing
def testGen():
    for i in xrange(size):
        doGen()


testAppend()
testAlloc()
testGen()

testAppend took 14440.000ms
testAlloc took 13580.000ms
testGen took 13430.000ms

5
"สำหรับโครงการของฉันการปรับปรุง 10% มีความสำคัญ"? จริงๆ? คุณสามารถพิสูจน์ได้ว่าการจัดสรรรายการเป็นคอขวด? ฉันต้องการดูรายละเอียดเพิ่มเติม คุณมีบล็อกที่คุณสามารถอธิบายได้ว่าสิ่งนี้ช่วยได้จริงหรือไม่?
S.Lott

2
@ S.Lott ลองเพิ่มขนาดตามลำดับความสำคัญ ประสิทธิภาพลดลง 3 ขนาดของคำสั่ง (เมื่อเทียบกับ C ++ ที่ประสิทธิภาพลดลงมากกว่าคำสั่งเดียวขนาดเล็กน้อย)
kfsone

2
อาจเป็นเพราะในกรณีที่อาเรย์เติบโตขึ้นมันอาจจะต้องถูกย้ายไปในหน่วยความจำ (คิดว่าวัตถุที่มีการจัดเก็บมีหนึ่งหลังจากที่อื่น.)
Evgeni Sergeev

3

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

พิจารณาจำนวนมากหากคุณทำการคำนวณเชิงตัวเลขกับรายการจำนวนมากและต้องการประสิทธิภาพ


0

สำหรับบางแอปพลิเคชันพจนานุกรมอาจเป็นสิ่งที่คุณกำลังมองหา ตัวอย่างเช่นในวิธี find_totient ฉันพบว่าการใช้พจนานุกรมสะดวกกว่าเพราะไม่มีดัชนีเป็นศูนย์

def totient(n):
    totient = 0

    if n == 1:
        totient = 1
    else:
        for i in range(1, n):
            if math.gcd(i, n) == 1:
                totient += 1
    return totient

def find_totients(max):
    totients = dict()
    for i in range(1,max+1):
        totients[i] = totient(i)

    print('Totients:')
    for i in range(1,max+1):
        print(i,totients[i])

ปัญหานี้สามารถแก้ไขได้ด้วยรายการที่จัดสรรล่วงหน้า:

def find_totients(max):
    totients = None*(max+1)
    for i in range(1,max+1):
        totients[i] = totient(i)

    print('Totients:')
    for i in range(1,max+1):
        print(i,totients[i])

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

เป็นความจริงที่พจนานุกรมจะไม่มีประสิทธิภาพ แต่อย่างที่คนอื่น ๆ ได้แสดงความคิดเห็นความแตกต่างเล็ก ๆ ของความเร็วนั้นไม่คุ้มกับการบำรุงรักษาที่สำคัญ


-1

จากสิ่งที่ฉันเข้าใจรายการหลามนั้นค่อนข้างคล้ายกับ ArrayLists แต่ถ้าคุณต้องการปรับเปลี่ยนพารามิเตอร์เหล่านั้นฉันพบโพสต์นี้ในเน็ตที่น่าสนใจ (โดยทั่วไปเพียงแค่สร้างScalableListส่วนขยายของคุณเอง):

http://mail.python.org/pipermail/python-list/2000-May/035082.html

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