Yanıtlar:
Bu bir var dinamik dizisi . Pratik kanıt: Endeksleme, (elbette son derece küçük farklarla (0.0013 µsec!)) İndekse bakılmaksızın aynı zamanı alır:
...>python -m timeit --setup="x = [None]*1000" "x[500]"
10000000 loops, best of 3: 0.0579 usec per loop
...>python -m timeit --setup="x = [None]*1000" "x[0]"
10000000 loops, best of 3: 0.0566 usec per loop
IronPython veya Jython bağlantılı listeler kullansaydı, listelerin dinamik diziler olduğu varsayımına dayanan birçok yaygın olarak kullanılan kütüphanenin performansını bozarlardı.
x=[None]*1000
, herhangi bir olası liste erişim farkının ölçümünü kesin olmayan bir şekilde bırakarak yapılır. Başlatma işlemini ayırmanız gerekir:-s "x=[None]*100" "x[0]"
C kodu aslında oldukça basit. Bir makroyu genişletmek ve bazı alakasız yorumları budamak, temel yapı, listobject.h
bir listeyi şu şekilde tanımlar:
typedef struct {
PyObject_HEAD
Py_ssize_t ob_size;
/* Vector of pointers to list elements. list[0] is ob_item[0], etc. */
PyObject **ob_item;
/* ob_item contains space for 'allocated' elements. The number
* currently in use is ob_size.
* Invariants:
* 0 <= ob_size <= allocated
* len(list) == ob_size
* ob_item == NULL implies ob_size == allocated == 0
*/
Py_ssize_t allocated;
} PyListObject;
PyObject_HEAD
bir başvuru sayısı ve bir tür tanımlayıcısı içerir. Yani, aşırı konumlanan bir vektör / dizi. Dolduğunda böyle bir diziyi yeniden boyutlandırma kodu listobject.c
. Aslında diziyi iki katına çıkarmaz, ancak ayırarak büyür
new_allocated = (newsize >> 3) + (newsize < 9 ? 3 : 6);
new_allocated += newsize;
her seferinde kapasiteye göre newsize
, istenen boyutun nerede olduğu ( tek tek değil, rastgele sayıda elemanla allocated + 1
yapabileceğiniz için değil ).extend
append
Ayrıca bkz. Python SSS .
array
modül veya NumPy tercih edilmelidir.
Bu uygulamaya bağlıdır, ancak IIRC:
ArrayList
Böylece hepsinin O (1) rasgele erişimi vardır.
O(1)
liste indeksleme oldukça yaygın ve geçerli bir varsayım olduğundan, hiçbir uygulama bunu kırmaya cesaret edemez.
Laurent Luce'ın "Python listesinin uygulanması" başlıklı makalesini öneririm . Benim için gerçekten yararlı oldu çünkü yazar listenin CPython'da nasıl uygulandığını açıklıyor ve bu amaçla mükemmel diyagramlar kullanıyor.
Liste nesnesi C yapısı
CPython'daki bir liste nesnesi aşağıdaki C yapısıyla temsil edilir.
ob_item
liste öğelerine işaretçilerin bir listesidir. ayrılan bellekte ayrılan yuva sayısıdır.typedef struct { PyObject_VAR_HEAD PyObject **ob_item; Py_ssize_t allocated; } PyListObject;
Ayrılan yuvalar ile listenin boyutu arasındaki farkın fark edilmesi önemlidir. Bir listenin boyutu ile aynıdır
len(l)
. Tahsis edilen yuvaların sayısı, hafızada tahsis edilen yuva sayısıdır. Genellikle, tahsis edilen boyuttan daha büyük olabilir. Bu,realloc
listeye her yeni öğe eklendiğinde çağrı yapılmasını önlemek içindir .
...
ekleme
Biz listeye bir tamsayı ekleyin:
l.append(1)
. Ne oluyor?
Biz bir daha eleman ekleyerek devam:
l.append(2)
.list_resize
n + 1 = 2 ile çağrılır, ancak ayrılan boyut 4 olduğundan daha fazla bellek ayırmaya gerek yoktur. Aynı şey 2 tam sayı daha eklediğimizde olur:l.append(3)
,l.append(4)
. Aşağıdaki şemada şu ana kadar sahip olduklarımız gösterilmektedir.
...
Ekle
Şimdi 1 konumuna yeni bir tamsayı (5) ekleyelim:
l.insert(1,5)
ve dahili olarak neler olduğuna bakalım.
...
Pop
Eğer son öğe pop zaman:
l.pop()
,listpop()
denir.list_resize
içeride çağrılırlistpop()
ve yeni boyut ayrılan boyutun yarısından daha azsa, liste küçülür.Yuva 4'ün hala tamsayıyı gösterdiğini gözlemleyebilirsiniz, ancak önemli olan şu anda 4 olan listenin boyutudur. Bir öğe daha açalım. Burada
list_resize()
, boyut - 1 = 4 - 1 = 3 ayrılan yuvaların yarısından daha azdır, bu nedenle liste 6 yuvaya küçültülür ve listenin yeni boyutu artık 3'tür.Yuva 3 ve 4'ün hala bazı tamsayıları işaret ettiğini gözlemleyebilirsiniz, ancak önemli olan şu anda 3 olan listenin boyutudur.
...
Kaldır Python liste nesnesi belirli bir öğe kaldırmak için bir yöntem vardır:
l.remove(5)
.
aggregation
değil composition
. Ben de bir kompozisyon listesi olsaydı.
Diğerlerinin yukarıda belirttiği gibi, listeler (oldukça büyük olduğunda) sabit bir alan tahsis edilerek ve bu alanın doldurulması gerekiyorsa, daha büyük bir alan tahsis edilerek ve öğelerin üzerine kopyalanarak uygulanır.
Yöntemin neden genelliği kaybetmeden O (1) itfa edildiğini anlamak için, bir = 2 ^ n öğesi eklediğimizi ve şimdi masamızı 2 ^ (n + 1) boyutuna ikiye katlamamız gerektiğini varsayın. Bu şu anda 2 ^ (n + 1) işlem yaptığımız anlamına geliyor. Son kopya, 2 ^ n işlem yaptık. Ondan önce 2 ^ (n-1) yaptık ... 8,4,2,1'e kadar. Şimdi, bunları toplarsak, 1 + 2 + 4 + 8 + ... + 2 ^ (n + 1) = 2 ^ (n + 2) - 1 <4 * 2 ^ n = O (2 ^ n) = O (a) toplam eklemeler (yani O (1) itfa edilmiş süre). Ayrıca, tablonun silmelere izin vermesi durumunda tablonun büzülmesinin farklı bir faktörde (örneğin 3x) yapılması gerektiğine dikkat edilmelidir.
Python'daki bir liste, birden çok değeri saklayabileceğiniz bir dizi gibi bir şeydir. Liste değiştirilebilir, yani değiştirebilirsin. Bilmeniz gereken en önemli şey, bir liste oluşturduğumuzda Python otomatik olarak bu liste değişkeni için bir başvuru_kimliği oluşturur. Başka bir değişken atayarak değiştirirseniz, ana liste değişecektir. Bir örnekle deneyelim:
list_one = [1,2,3,4]
my_list = list_one
#my_list: [1,2,3,4]
my_list.append("new")
#my_list: [1,2,3,4,'new']
#list_one: [1,2,3,4,'new']
Ekliyoruz my_list
ama ana listemiz değişti. Bu ortalamanın listesi referans olarak bir kopya listesi ataması olarak atanmadı.