Initializing a list to a known number of elements in Python
现在,我正在使用一个列表,并期望如下:
我应该改用数组吗?
- 使用预定义数量的元素初始化集合。
- 为什么?你必须在任意位置设置元素吗?
- 知道尺寸没什么用。我们通常使用字典来处理这类事情,并且不浪费时间创建大型的、空的结构。
- 为什么?我收集了一些重要的物品。你们知道怎么做的答案吗?史蒂夫的回答似乎是唯一的办法。
- 我很惊讶(对@joanvenge感到有点抱歉)到处都是数字评论。在我看来,一个标准的答案应该首先包括如何完成一项任务(不管它有多荒谬),然后就这个问题向用户提出警告/建议。这似乎毫无意义。质疑问题的有效性是可以质疑的。
- @沙山克萨万:欢迎来到这里。
- 我有一个用例,在这个用例中,我必须在2次传递中填写一个列表。传递1用值填充某些已知索引。第2遍填充其余部分,跳过上一遍填充的项。我不认为我可以在特定的索引中填充值,除非我初始化列表。
我首先想到的是:
但您真的需要预初始化它吗?
- 这不是"需要",只是(过早地?)优化
- 如果您正在编写新代码,这不是一个过早的优化。
- 这就像是说,不关心性能,只写它。
- 是的,这正是关键所在。"过早的优化是万恶之源,"这意味着你首先应该在不考虑性能的情况下编写代码。"如果稍后发现代码运行缓慢,请返回并进行类似这样的优化。
- 我认为你错了。当你试图改变已经起作用的东西时,就会发生过早的优化。您应该总是尽可能快地编写代码。
- 不,过早的优化是指试图优化不确定需要优化的代码。您不应该总是尽可能快地编写代码——其他的关注点,如业务目标、维护成本、编写代码的工程时间,通常更为重要。
- 即使你的话也能证明我的。当你试图优化代码时。在我的例子中,由于没有代码,显然我不能过早地进行优化。
- 引用我的一个朋友彼得·里奇的话:"这是对一个被误解的引语最大的误解。它是霍尔,引用的是:"我们应该忘记小效率,说大约97%的时间:过早的优化是万恶之源。"其目的是
- 对性能的微小改进所做的微小改变并不能证明所引入的不稳定性是合理的。但是,在编写原始代码时,应该始终使用性能最好的算法来编写。
- "我们应该忘记低效率"——预先初始化1000个列表还为时过早。
- 1000只是一个例子。
- 请注意,除了对希望预分配数组的优化之外,还有其他合法的情况。它可能是使用它的代码,不添加元素,只替换现有的元素,所以它更像是一个数组而不是一个列表。
- 也许这与优化无关。例如,在动态编程中,预初始化列表很有用。(不过数组会更好。)
- 这种初始化python数组的方法是邪恶的:a=[[]]*2; a[0].append('foo');现在检查a[1],你会震惊的。相比之下,a=[[] for k in range(2)]工作得很好。
- 如果你有一个图形算法和少量的内存,唯一重要的是位置是否被访问。这对很多现代的东西都很有用。
- 检查你的假设。例如,我目前正在分析一个网络错误率,通过分析一个日志文件并将错误放入一组容器中,当前为4个容器/小时和24小时/天。一天中的小时数不变,如果我每小时更换垃圾箱,我将停止并重新启动程序,因此我总是希望(当前)4*24=96个垃圾箱。对我来说(用C/C++ +C语言等)从初始化每个bin到0开始是很自然的。这是如何进行优化的,无论是否过早?
- 正确使用内存并不是邪恶的过早优化。诚然,初学者不应该过早地对细节感到厌烦,但我是一个经验丰富的程序员,如果我知道我想要一个特定大小的列表,而且不需要额外的努力就能使它达到正确的大小,我就要这样做。确保我不会对资源感到愚蠢,这样可以防止我以后在编写的软件扩展到用户基础和功能时遇到奇怪的错误。真正的问题是过早的优化,这会降低代码的可读性和可维护性。
不太清楚为什么每个人都让你很难做到这一点——有几种情况下你需要一个固定大小的初始化列表。您已经正确地推断出数组在这些情况下是合理的。
1 2
| import array
verts=array.array('i',(0,)*1000) |
对于非pythonistas,(0,)*1000术语正在创建一个包含1000个零的元组。逗号强制python将(0)识别为元组,否则将被评估为0。
我使用了元组而不是列表,因为它们的开销通常较低。
- 我想有些人会采取"过早"的优化。
- 谢谢!这个解决方案正是我想要的。在分析时,列表初始化是我代码中的瓶颈,这使它快了2倍。
- 遗憾的是,我还没有找到一个关于python问题的答案,所以它没有包含一些自鸣得意的内容:"为什么要这样做?"-类型宿舍房间傲慢作为标准反应。"社区"。
- @在世界上许多国家,包括法国、西班牙和荷兰,圣女贞德是一个男性名字。
- @克里斯对西班牙和我所知的所有国家来说都是如此。不知道法国,在那里住了很多年。如果我稍微修改一下我的话,这种特别恼人的攻击语气可能是因为一些(英语使用者)"宿舍骑师"假设琼是女性。
- 这个答案看起来不错,但今天速度太慢了。在python2.7中,a=[0]*10000000比array.array快几倍("i",(0,)*10000000)
一个明显的而且可能不有效的方法是
1
| verts = [0 for x in range(1000)] |
注意,这可以很容易地扩展到二维。例如,要获得一个10x100"阵列",您可以这样做
1
| verts = [[0 for x in range(100)] for y in range(10)] |
在任何编程语言中,想要初始化一个固定大小的数组是完全可以接受的事情;这不像程序员想要在while(true)循环中放入break语句。相信我,尤其是如果元素只是要被覆盖,而不仅仅是加/减,就像许多动态编程算法一样,你不想乱弄APPEND语句,检查元素是否还没有被动态初始化(这是很多代码代理)。
object = [0 for x in range(1000)]
这将适用于程序员试图实现的目标。
- + 1。我担心如果我做了正确的事情,用预先定义的大小初始化数组。你的回答使我平静。
@史蒂夫已经给了你一个很好的答案:
警告:正如@joachim wutke指出的,列表必须用不可变元素初始化。[[]] * 1000无法按预期工作,因为您将获得1000个相同列表的列表(类似于C中相同列表的1000点列表)。像int、str或tuple这样的不可变对象可以做得很好。
选择
调整列表大小很慢。以下结果并不令人惊讶:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| >>> N = 10**6
>>> %timeit a = [None] * N
100 loops, best of 3: 7.41 ms per loop
>>> %timeit a = [None for x in xrange(N)]
10 loops, best of 3: 30 ms per loop
>>> %timeit a = [None for x in range(N)]
10 loops, best of 3: 67.7 ms per loop
>>> a = []
>>> %timeit for x in xrange(N): a.append(None)
10 loops, best of 3: 85.6 ms per loop |
但是,如果没有非常大的列表,那么调整大小不是很慢。不要用单个元素(如None和固定长度初始化列表以避免列表大小调整,应考虑使用列表理解并直接用正确的值填充列表。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| >>> %timeit a = [x**2 for x in xrange(N)]
10 loops, best of 3: 109 ms per loop
>>> def fill_list1():
"""Not too bad, but complicated code"""
a = [None] * N
for x in xrange(N):
a[x] = x**2
>>> %timeit fill_list1()
10 loops, best of 3: 126 ms per loop
>>> def fill_list2():
"""This is slow, use only for small lists"""
a = []
for x in xrange(N):
a.append(x**2)
>>> %timeit fill_list2()
10 loops, best of 3: 177 ms per loop |
与numpy比较
对于大型数据集,numpy或其他优化的库速度更快:
1 2 3 4 5 6
| from numpy import ndarray, zeros
%timeit empty((N,))
1000000 loops, best of 3: 788 ns per loop
%timeit zeros((N,))
100 loops, best of 3: 3.56 ms per loop |
你可以这样做:
1
| verts = list(xrange(1000)) |
这将给您一个1000个元素的大小列表,并且恰好用0-999之间的值初始化。正如list首先对新列表进行尺寸调整一样,它应该是相当有效的。
- 在python3.0之前,它是范围(1000);在python3.0中,它是列表(范围(1000))。
这是:
1
| lst = [8 for i in range(9)] |
创建列表,初始化元素8
但这:
将创建7个具有一个元素的列表
- [0] * 7的计算结果是[0, 0, 0, 0, 0, 0, 0],它是一个包含7个元素的列表。或者您描述的是一些非常老的Python版本的行为?
- 他说的是列表包含7个元素,但所有7个元素都指向同一个内存。对这7个元素中的任何一个进行修改都会导致其他元素发生相应的变化。
- 如果元素是整数,不是吗?我刚试过mylist = [0] * 4,然后在mylist[0] = 12之后,mylist返回[12, 0, 0, 0]。
您应该考虑使用dict类型,而不是预先初始化的列表。字典查找的成本很小,与访问任意列表元素的成本相当。
当使用映射时,您可以编写:
1 2 3
| aDict = {}
aDict[100] = fetchElement()
putElement(fetchElement(), fetchPosition(), aDict) |
putElement函数可以在任意给定位置存储项目。如果您需要检查集合中是否包含给定索引处的元素,那么可以编写更多的pythonic:
1 2
| if anIndex in aDict:
print"cool!" |
比:
1 2
| if not myList[anIndex] is None:
print"cool!" |
因为后者假定集合中没有真正的元素可以是None。如果发生这种情况,你的代码就会出现错误。
如果你非常需要性能,这就是为什么你要尝试预先初始化变量,并尽可能快地编写代码——改变你的语言。最快的代码不能用Python编写。您应该改为尝试c并实现包装器来从python调用预初始化和预编译的代码。
如果不了解问题领域的更多信息,很难回答您的问题。除非您确定需要做更多的事情,否则初始化列表的方法是:
你真的看到性能问题了吗?如果是,那么性能瓶颈是什么?不要试图解决你没有的问题。动态地将数组填充到1000个元素中的性能成本很可能与您真正想要编写的程序完全无关。
如果列表中的内容总是特定的基元固定长度类型(例如char、int、float),则array类非常有用。但是,它也不需要预初始化。
- 你不明白这一点。我只想创建一个具有预先定义的元素数量的列表/数组。评论我为什么和如何需要是愚蠢的。我知道我在做什么。谢谢。
- 当我说,我知道我在做什么,我的意思是编程明智,而不是Python。如果我认识python,我就不会问这个问题了,是吗?
- 你能编辑这个问题并解释更多的上下文吗?从这个问题来看,不清楚正确的答案是什么,也不清楚你知道自己在做什么。