在Python中,当收益成本超过返回列表时?
在许多情况下,人们总是说“使用在Python中,当收益成本超过返回列表时?,python,Python,在许多情况下,人们总是说“使用yield惰性地创建元素。” 但我认为一切都是有代价的,包括yield及其迭代器 在诺德看来,我认为这是个好问题。 例如,当我得到一个函数时 def list_gen(n): if n > MAGIC_NUM: return xrange(n) else: return range(n) 这个神奇的数字是多少 更新很抱歉,我的意思是比较迭代器的成本和列表成本 再次更新请想象一个案例。是否有一个条件,即内存的限制
yield
惰性地创建元素。”
但我认为一切都是有代价的,包括yield
及其迭代器
在诺德看来,我认为这是个好问题。
例如,当我得到一个函数时
def list_gen(n):
if n > MAGIC_NUM:
return xrange(n)
else:
return range(n)
这个神奇的数字是多少
更新很抱歉,我的意思是比较迭代器的成本和列表成本
再次更新请想象一个案例。是否有一个条件,即内存的限制使其无法创建迭代器
哈,这个问题现在更有趣了。再次更新为什么创建迭代器和保存产量上下文比创建列表要少?或者迭代器要花多少钱?(对不起我的侮辱)多少字节?它与您正在生成的迭代器的长度无关,而是与您以后需要如何使用它有关。如果你只需要使用一次,那么你肯定应该选择收益率,如果你想多次使用它,你可以跳过收益率,只需要获得一个常规列表。请记住,使用yield获得的生成器只能使用
yield
迭代一次,或者生成器与列表大小基本无关,例如:
- 如果您不需要处理整个列表,并且可能很快就会中断,那么使用生成器会更有效
- 模拟无限大小的流,例如质数生成器
至于成本,使用生成器还有一个额外的成本,如果您计算每次调用生成器时评估对生成器的调用的成本,但是使用列表将占用更多内存,因此一般来说,您不能说生成器比列表更好,因为它涉及内存和性能之间的一些权衡,是否使用生成器取决于您的需要和具体情况。请注意,不能同时使用
收益率
和收益率
。函数可以是生成器函数或普通函数,但不能同时是两者
通常,yield
避免创建中间列表,而是逐个生成元素。例如,在递归遍历树时,这可能很有用。有关示例,请参见此链接:
生成器的另一个用途是当您希望返回大量元素,但您的用户可能只对前几个元素感兴趣(例如搜索结果)
避免使用中间列表将节省内存,但前提是调用方不需要根据结果创建列表。总的来说,它的优点是,它可以让您更精确地编写生成器函数。您将多种内容混为一谈
def list_gen(n):
i=0
while i<n:
yield i
i += 1
这些函数是常规函数。一个返回列表
,另一个返回xrange
对象。列表和xRange都是可编辑的,即可以为它们创建多个独立的迭代器
回到你的问题:你在问是返回
列表
还是返回xrange
对象
那要看情况,显然!这取决于你想对结果做什么
- 如果你想以某种方式改变它,那么你需要一个真正的列表。直接使用
范围
- 如果您只想对它进行迭代,那么它在语义上没有什么区别:一个
对象和一个xrange
返回的range
都会生成一个迭代器,它在同一序列上进行迭代 但是,如果使用list
,则永远不会在内存中创建整个列表。如果您只想做一次简单的迭代,为什么要在内存中创建一个完整的xrange
列表
对象呢?每当您想要
循环时,您不需要分配临时大内存缓冲区,对吗for
xrange
是安全的,因为调用者总是可以从中列出列表。
让我们用一个基准来确认这一点。我们想知道在xranges上迭代是否比在
range
构建的列表上迭代更快(当然包括range
调用的成本)
代码:
结果:
n range xrange
1 0.566222990493 0.418698436395
2 0.594136874362 0.477882061758
3 0.630704800817 0.488603362929
5 0.725149288913 0.540597548519
10 0.90297752809 0.687031507818
50 2.44493085566 1.89102105759
100 4.31189321914 3.33713522433
虽然你的问题和它的标题仍然有点混淆,但我会尽力以我理解的方式回答 如果您只想迭代
(x)range()
,则xrange()
(特殊对象)比range()
(列表)更适用于较短和较长的范围:
$ python -m timeit 'a=range(3)' 'for i in a: pass'
1000000 loops, best of 3: 0.608 usec per loop
$ python -m timeit 'a=xrange(3)' 'for i in a: pass'
1000000 loops, best of 3: 0.466 usec per loop
$ python -m timeit 'a=xrange(30000)' 'for i in a: pass'
1000 loops, best of 3: 1.01 msec per loop
$ python -m timeit 'a=range(30000)' 'for i in a: pass'
1000 loops, best of 3: 1.49 msec per loop
因此最好始终使用xrange()
如果您看一看一般情况,可能会略有不同:您比较“预生成”值/对象,将它们存储在列表中,然后进行处理,以及在生产后直接使用它们:
def gen(num):
import random
i = 0
while i < num:
value = random.random()
yield value
i += 1
def process(value): pass
def test1(num):
data = list(gen(num))
for i in data: process(num)
def test2(num):
for i in gen(num): process(num)
运行生产并消耗所有项目,因为它们在这里,不需要任何时间来生产或消耗它们。您的意思是
返回xrange(n)
或返回范围(n)
对吗?@Kos是的,我正在修复这个错误。生成器使用的内存比列表少。因此,如果内存非常有限,无法创建迭代器,那么它也无法创建列表。您的最后一个问题与其他问题完全不同,并且不适合stackoverflow,因为stackoverflow不适合“为什么”问题。但产出上下文只是一个堆栈级别。与创建包含一组对象的列表相比,它不是资源密集型的。即使是一个整数也有12个字节。虽然一切都是正确的,但你根本没有解决这个问题,也没有解决提问者问题、代码和假设中的错误。@Lennartreegebro I
$ python -m timeit 'a=range(3)' 'for i in a: pass'
1000000 loops, best of 3: 0.608 usec per loop
$ python -m timeit 'a=xrange(3)' 'for i in a: pass'
1000000 loops, best of 3: 0.466 usec per loop
$ python -m timeit 'a=xrange(30000)' 'for i in a: pass'
1000 loops, best of 3: 1.01 msec per loop
$ python -m timeit 'a=range(30000)' 'for i in a: pass'
1000 loops, best of 3: 1.49 msec per loop
def gen(num):
import random
i = 0
while i < num:
value = random.random()
yield value
i += 1
def process(value): pass
def test1(num):
data = list(gen(num))
for i in data: process(num)
def test2(num):
for i in gen(num): process(num)
def list_eater(l):
while l:
yield l.pop(0)
def test3(num):
data = []
def producer():
for i in gen(num): data.append(i)
import threading
consumerthread = threading.Thread(target=producer)
consumerthread.start()
while data or consumerthread.isAlive():
for item in list_eater(data): process(item)
# Optimizeable. Does idle waiting; a threading.Condition might be quite useful here...