在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...