Python 3.3的收益率来自

时间:2012-09-08 13:32:54

标签: python functional-programming yield

Python 3带来了yield from语义。据我所知,它应该屈服于最外层的生成器,在这种情况下,我希望这段代码在N中是线性的。

from collections import Iterable

def flatten(L):
  for e in L:
    if isinstance(e, Iterable):
      yield from flatten(e)
    else:
      yield e 

N = 100
L = [-1]
for i in range(N):
  L = [i, [L], i]
for i in range(100):
  f = list(flatten(L))
print(len(f))

如果我设置N=200但计算时间大约是计算时间的四倍,表明展平是L长度的二次方。我无法理解为什么会这样,因为代码只访问每个元素一次,而yield from关键字应该直接从内部生成器发送值到将列表收集到列表中。

这是一个错误,根本不是故意的,还是我使用它错了?有没有一种很好的方法在Python中进行O(N)展平?

1 个答案:

答案 0 :(得分:14)

yield from,就像for item in x: yield x一样,线性的。但是,函数调用很慢,并且由于l中的嵌套,当你加倍N时,你不仅仅是术语数量加倍,而是需要调用的次数增加一倍。任何随着调用次数而扩展的东西,比如函数开销本身。由于递归,任何yield from开销,for循环初始化开销,无论如何,都会导致问题。如果这是正确的,那么我们期望具有相同数量的元素且没有嵌套的列表将是线性的,中间嵌套将介于两者之间,并且许多嵌套将是超慢的。这就是我们所看到的:

import timeit

s = """

from collections import Iterable

def flatten(l):
   for e in l:
       if isinstance(e, Iterable):
           yield from flatten(e)
       else:
           yield e 

def build_lots(N):
    l = [-1]
    for i in range(N):
        l = [i, [l], i]
    return l

def build_some(N):
    l = [-1]
    for i in range(N):
        l = [i]+l+[i] if i % 2 else [i,[l],i]
    return l

def build_none(N):
    return range(2*N+1)

"""

def get_time(size, which_build, n=100):
    setup = s + "l = build_{}({});".format(which_build, size)
    ans = timeit.Timer(setup=setup, stmt="z=list(flatten(l))").timeit(n)
    return ans

print('length', 'none','some','lots')
for w in range(0, 500, 50):
    print(2*w+1, 
          get_time(w, 'none'), 
          get_time(w, 'some'),
          get_time(w, 'lots'))

产生

length none some lots
1 0.0012789969332516193 0.0006600483320653439 0.000653265044093132
101 0.030214487109333277 0.06863495009019971 0.10554128512740135
201 0.05980244372040033 0.188231083098799 0.3237948380410671
301 0.08960435865446925 0.3752179825678468 0.6493003228679299
401 0.11986956000328064 0.6066137161105871 1.147628225851804
501 0.14737469609826803 0.9323012446984649 1.7087256000377238
601 0.18555369088426232 1.2575508910231292 2.2957410947419703
701 0.20820995513349771 1.712264522910118 3.094764341134578
801 0.23618148919194937 2.100640726275742 4.079551971051842
901 0.26863432209938765 2.617169467266649 4.858607416041195

simple plot of time vs nesting