在python

时间:2018-04-21 14:11:17

标签: python list

在python中制作包含整数/浮点数(非常简单的数据类型)的类似列表对象的最快方法是什么?

“list-like”是什么意思?

这意味着我希望有一个对象支持列表的两个(非常)基本操作:获取某个索引(1)中的对象并更改其值(2)。

在发布此帖子之前我遇到了什么帖子,为什么他们没有解决我的问题?

我遇到了这两个:[1] [2]

他们没有解决我的问题,因为他们的所有解决方案都太慢了:在我的电脑array.array('i',(0,)*10 ** 8)导致错误(笑); [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. 您必须导入模块才能使用它
  4. 我真的可以做我要问的事吗?是否有更快的方式而不是使用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)]
    

5 个答案:

答案 0 :(得分:4)

  

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__将比列表慢几个数量级。或array.array。对您来说哪个更好,归结为您的计划中更频繁的操作,这取决于您的发现。

答案 1 :(得分:3)

我只使用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

答案 2 :(得分:2)

我会说你可以尝试不同的方法:

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

答案 3 :(得分:2)

如果确实只想要这两个属性:

  

获取某个索引(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
然而,实际使用它可能会比其他答案中提出的任何类型都慢一些。

答案 4 :(得分:0)

对于只需要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不同,它直接将分配归零,而不是从用零初始化的对象进行复制。