簡體   English   中英

在python中創建包含整數的類似列表的對象的最快方法

[英]Fastest way to make a list-like object containing integers in python

在python中創建包含整數/浮點數(非常簡單的數據類型)的類似列表的對象的最快方法是什么?

“列表式”是什么意思?

這意味着我想擁有一個支持列表的兩個(非常)基本操作的對象:獲取某個索引(1)中的對象並更改其值(2)。

在發布此帖之前我遇到了什么帖子,為什么他們沒有解決我的問題?

我遇到了這兩個: [1] [2]

他們沒有解決我的問題,因為他們的所有解決方案都太慢了:在我的PC array.array('i',(0,)*10 ** 8)導致錯誤(lol); [0 for _ in range(10**8)]花了大約15秒(哇!); [0] * 10 ** 8耗時2.3秒; [None] * 10 ** 8了1.8秒; (1.8秒可能更快...)

我嘗試做什么?

我嘗試使用ctypes模塊

from ctypes import c_int
array = (c_int * 10 ** 8)()

上面的代碼只花了0.7秒......但有沒有辦法讓它更快? 除了快速,它有一些缺點:

  1. 因為它使用了c / c ++變量的骨架,所以它中的整數將處於“不像python一樣無限”的整數值范圍內
  2. 您不能在列表中擁有多個數據類型
  3. 您必須導入模塊才能使用它

真的有可能做我要問的事嗎? 有沒有比使用ctypes模塊更快的方法? 如果是這樣,請確保使用“內置”/“預安裝”模塊。

編輯:

為什么我不能簡單地安裝一些模塊,比如numpy?

我正在使用python進行競爭性編程,大多數口譯/評委都不允許使用外部庫。

我們可以使用array.array存儲自定義對象嗎?

我可以看到很多答案都使用了array模塊的array功能。 他們都使用'i'來指定我們想要存儲整數。 是否可以創建一個類並創建一個包含它的`array.array'? 例如:

class Point:
 def __init__(self, x, y):
  self.x = x
  self.y = y

# make array.array object with all indexes containing a Point with atributes x and y with value 0
# an example with a list of what I want to do is this:
# l = [Point(0, 0) for _ in range(10**3)]

array.array('i',(0,) * 10**8)導致錯誤(lol)

你沒有指定你得到的錯誤 - 這對我有用,雖然它不是很快,因為它構建了一個中間元組並立即丟棄它。 使用Python的內置類型,如果你避免使用元組, array.array可能會產生最佳性能:

a = array.array('i', (0,)) * 10**8

上面的代碼只花了0.7秒......但有沒有辦法讓它更快?

如果不允許創建或導入C擴展,那將很難擊敗array.array 在我幾年前的機器上,上面需要0.6秒。 您可以通過增加初始數組的大小來進一步優化它。 例如,這會產生相同的結果,但幾乎快3倍(!):

# 0.22 s
a = array.array('i', (0,) * 10) * 10**7

在我的機器上,以下版本效果最佳:

# 0.19 s
a = array.array('i', (0,) * 100) * 10**6

進一步增加初始陣列大小並沒有幫助,很快就會開始降低性能。

為了獲得更高的效率,請考慮其他方法,例如惰性列表或為您的用例量身定制的完全不同的數據結構。 鑒於競爭的背景,這可能是實際上正在尋求的。

但請注意,每種解決方案都會有不同的權衡。 例如,像@KonstantinNikitin提供的惰性數組將非常有效地構造,但是在純Python中實現的__getitem____setitem__將比list或array.array慢幾個數量級。 對您來說哪個更好,歸結為您的計划中更頻繁的操作,這取決於您找出答案。

我會使用numpy模塊,它支持快速數組操作。

例如,制作一個數字為0到10 ** 8的數組:

import numpy as np
import time

b = time.time()
a = np.linspace(0, 10**8, 10**8)
c = time.time()
print(c-b)
>>>0.5000154972076416

或者制作一個長度為10 ** 8的0數組:

b = time.time()
a = np.zeros(shape=(10**8,))
c = time.time()
print(c-b)
>>>0.0

numpy這么快的主要原因是因為它是用C實現的。

編輯:如果你想只使用預安裝的包,你可以嘗試使用array包:

import array
import time
r = time.time()
a = array.array('i', [0]) * (10**8)
print(time.time()-r)
>>>0.15627217292785645

我會說你可以嘗試不同的方法:

1) numpy 它確實是陣列的標准。 它為每個操作帶來了跨越Python < - > C邊界的成本,但它實際上取決於您的任務。

x = numpy.array(10 ** 8)

timeit.timeit('x = numpy.array(10 ** 8)', 'import numpy', number=1)
4.195800283923745e-05

2)延遲初始化(如JavaScript數組)。

class LazyArray:
    def __init__(self, size):
        self.storage = {}
        self.size = size

    def check(self, i):
        if i < 0 or i >= self.size:
            raise RuntimeError() 

    def __getitem__(self, i):
        self.check(i)
        return self.storage.get(i, 0)

    def __setitem__(self, i, value):
        self.check(i)
        self.storage[i] = value

x = LazyArray(10 ** 8)
x[10]
>> 0
x[10] = 5
x[10]
>> 0

如果你真的只想要這兩個屬性:

獲取某個索引(1)中的對象並更改其值(2)

然后你可以使用collections.defaultdict

import collections
my_list = collections.defaultdict(lambda: 0)

相當快(~0.4μs):

$ python3 -m timeit -s 'import collections' 'collections.defaultdict(lambda: 0)' 
1000000 loops, best of 3: 0.417 usec per loop

但是,實際使用它可能比其他答案中建議的任何類型都要慢一些。

對於只需要0到255之間的整數的情況,可以非常快速地創建bytearray對象:

>>> timeit.timeit('bytearray(100000)', number=1000)
0.005567271093696036
>>> timeit.timeit('array.array("B", [0])*100000', 'import array', number=1000)
0.36631167401839093
>>> timeit.timeit('array.array("i", [0])*100000', 'import array', number=1000)
0.56494557472422

array.array不同,這會直接將分配歸零,而不是從使用零初始化的對象進行復制。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM