2014-02-21 32 views
3

寫了一篇關於一些python編碼風格的博客文章,發現了一些我覺得很奇怪的東西,我想知道是否有人明白它發生了什麼。基本上我有兩個版本的相同功能:爲什麼這個lambda函數比for循環版本更復雜?

a = lambda x: (i for i in range(x)) 
def b(x): 
    for i in range(x): 
     yield i 

而我想比較這兩個剛剛建立的性能。在我的腦海裏這應該涉及到計算的微不足道的金額與方法應拿出相當接近零,但是,當我居然跑使用timeit:

def timing(x, number=10): 
    implicit = timeit.timeit('a(%s)' % int(x), 'from __main__ import a', number=number) 
    explicit = timeit.timeit('b(%s)' % int(x), 'from __main__ import b', number=number) 
    return (implicit, explicit) 

def plot_timings(*args, **kwargs): 
    fig = plt.figure() 
    ax = fig.add_subplot(1,1,1) 
    x_vector = np.linspace(*args, **kwargs) 
    timings = np.vectorize(timing)(x_vector) 
    ax.plot(x_vector, timings[0], 'b--') 
    ax.plot(x_vector, timings[1], 'r--') 
    ax.set_yscale('log') 
    plt.show() 

plot_timings(1, 1000000, 20) 

我得到的兩種方法之間的巨大差異,如下圖所示:

<code>a</code> is in blue, <code>b</code> is in red

a是藍色的,b是紅色。

爲什麼差異那麼大?它看起來明確的for循環版本也是對數增長,而隱式版本什麼都不做(因爲它應該)。

有什麼想法?

+0

什麼軸呢? –

+1

我認爲你在幾個陳述中倒退了?拉姆達版本是對數增長的版本。 – roippi

回答

2

差由range

a需要引起調用範圍,當你建造它。
b並不需要調用範圍,直到第一次迭代

>>> def myrange(n): 
...  print "myrange(%s)"%n 
...  return range(n) 
... 
>>> a = lambda x: (i for i in myrange(x)) 
>>> def b(x): 
...  for i in myrange(x): 
...   yield i 
... 
>>> a(100) 
myrange(100) 
range(100) 
<generator object <genexpr> at 0xd62d70> 
>>> b(100) 
<generator object b at 0xdadb90> 
>>> next(_) # <-- first iteration of b(100) 
myrange(100) 
range(100) 
0 
0

拉姆達調用是緩慢的。檢查了這一點:

import cProfile 

a = lambda x: (i for i in range(x)) 

def b(x): 
    for i in range(x): 
     yield i 

def c(x): 
    for i in xrange(x): 
     yield i 

def d(x): 
    i = 0 
    while i < x: 
     yield i 
     i += 1 


N = 100000 
print " -- a --" 
cProfile.run(""" 
for x in xrange(%i): 
    a(x) 
""" % N) 

print " -- b --" 
cProfile.run(""" 
for x in xrange(%i): 
    b(x) 
""" % N) 

print " -- c --" 
cProfile.run(""" 
for x in xrange(%i): 
    c(x) 
""" % N) 

print " -- d --" 
cProfile.run(""" 
for x in xrange(%i): 
    d(x) 
""" % N) 

print " -- a (again) --" 
cProfile.run(""" 
for x in xrange(%i): 
    a(x) 
""" % N) 

給了我下面的結果:

-- a -- 
     300002 function calls in 61.764 seconds 

    Ordered by: standard name 

    ncalls tottime percall cumtime percall filename:lineno(function) 
     1 30.881 30.881 61.764 61.764 <string>:3(<module>) 
    100000 0.051 0.000 0.051 0.000 test.py:5(<genexpr>) 
    100000 0.247 0.000 30.832 0.000 test.py:5(<lambda>) 
     1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} 
    100000 30.585 0.000 30.585 0.000 {range} 


-- b -- 
     100002 function calls in 0.076 seconds 

    Ordered by: standard name 

    ncalls tottime percall cumtime percall filename:lineno(function) 
     1 0.066 0.066 0.076 0.076 <string>:3(<module>) 
    100000 0.010 0.000 0.010 0.000 test.py:7(b) 
     1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} 


-- c -- 
     100002 function calls in 0.075 seconds 

    Ordered by: standard name 

    ncalls tottime percall cumtime percall filename:lineno(function) 
     1 0.065 0.065 0.075 0.075 <string>:3(<module>) 
    100000 0.010 0.000 0.010 0.000 test.py:11(c) 
     1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} 


-- d -- 
     100002 function calls in 0.075 seconds 

    Ordered by: standard name 

    ncalls tottime percall cumtime percall filename:lineno(function) 
     1 0.065 0.065 0.075 0.075 <string>:3(<module>) 
    100000 0.010 0.000 0.010 0.000 test.py:15(d) 
     1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} 


-- a (again) -- 
     300002 function calls in 60.890 seconds 

    Ordered by: standard name 

    ncalls tottime percall cumtime percall filename:lineno(function) 
     1 30.487 30.487 60.890 60.890 <string>:3(<module>) 
    100000 0.049 0.000 0.049 0.000 test.py:5(<genexpr>) 
    100000 0.237 0.000 30.355 0.000 test.py:5(<lambda>) 
     1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects} 
    100000 30.118 0.000 30.118 0.000 {range} 
相關問題