python lambdas与标准函数的实现方式有何不同?

Sam*_*lar 7 python lambda function

在尝试为另一个SO问题写一个答案的时候发生了一件非常奇怪的事情.

我基本上想出了一个内置gcd并说 it maybe slower because of recursion
gcd = lambda a,b : a if not b else gcd(b, a % b)

这是一个简单的测试:

assert gcd(10, 3) == 1 and gcd(21, 7) == 7 and gcd(100, 1000) == 100
Run Code Online (Sandbox Code Playgroud)

这里有一些基准:

timeit.Timer('gcd(2**2048, 2**2048+123)', setup = 'from fractions import gcd').repeat(3, 100)
# [0.0022919178009033203, 0.0016410350799560547, 0.0016489028930664062]
timeit.Timer('gcd(2**2048, 2**2048+123)', setup = 'gcd = lambda a,b : a if not b else gcd(b, a % b)').repeat(3, 100)
# [0.0020480155944824219, 0.0016460418701171875, 0.0014090538024902344]
Run Code Online (Sandbox Code Playgroud)

那很有意思,我预计会慢很多,但时间相当接近,?也许导入模块是问题...

>>> setup = '''
... def gcd(a, b):
...     """Calculate the Greatest Common Divisor of a and b.
... 
...     Unless b==0, the result will have the same sign as b (so that when
...     b is divided by it, the result comes out positive).
...     """
...     while b:
...         a, b = b, a%b
...     return a
... '''
>>> timeit.Timer('gcd(2**2048, 2**2048+123)', setup = setup).repeat(3, 100)
[0.0015637874603271484, 0.0014810562133789062, 0.0014750957489013672]
Run Code Online (Sandbox Code Playgroud)

nope仍然相当接近时间ok让我们尝试更大的价值观.

timeit.Timer('gcd(2**9048, 2**248212)', setup = 'gcd = lambda a,b : a if not b else gcd(b, a % b)').repeat(3, 100) [2.866894006729126, 2.8396279811859131, 2.8353509902954102]
[2.866894006729126, 2.8396279811859131, 2.8353509902954102]
timeit.Timer('gcd(2**9048, 2**248212)', setup = setup).repeat(3, 100)
[2.8533108234405518, 2.8411397933959961, 2.8430981636047363]
Run Code Online (Sandbox Code Playgroud)

有趣的我不知道最近会发生什么?
由于调用函数的开销,我总是假设递归速度较慢,lambda是异常吗?为什么我没有达到我的递归限制?
如果实现使用def我立即点击它,如果我将递归深度增加到像10**9我实际上得到segmentation fault一个堆栈溢出...

更新

>>> setup = '''
... import sys
... sys.setrecursionlimit(10**6)
... 
... def gcd(a, b):
...     return a if not b else gcd(b, a % b)
... '''
>>> 
>>> timeit.Timer('gcd(2**9048, 2**248212)', setup = 'gcd = lambda a,b:a if not b else gcd(b, a%b)').repeat(3, 100)
[3.0647969245910645, 3.0081429481506348, 2.9654929637908936]
>>> timeit.Timer('gcd(2**9048, 2**248212)', setup = 'from fractions import gcd').repeat(3,   100)
[3.0753359794616699, 2.97499680519104, 3.0096950531005859]
>>> timeit.Timer('gcd(2**9048, 2**248212)', setup = setup).repeat(3, 100)
[3.0334799289703369, 2.9955930709838867, 2.9726388454437256]
>>> 
Run Code Online (Sandbox Code Playgroud)

更令人费解的......

Nik*_* B. 6

counter = 0

def gcd(a, b):
    global counter
    counter += 1
    return a if not b else gcd(b, a % b)

gcd(2**9048, 2**248212)
print counter
Run Code Online (Sandbox Code Playgroud)

打印3.当然,深度3的递归没有太多开销.


Mar*_*cin -1

lambda 的类型与任何其他函数的类型完全相同,并且在这两种情况下,如果在另一个本地作用域中定义,则会发生环境捕获。

唯一的区别是,使用 lambda 语法定义的函数不会自动成为其出现范围内变量的值,并且 lambda 语法要求主体是一个(可能是复合)表达式,并返回其值从函数。

至于递归的速度 - 是的,有一点开销,但显然没有那么多。调用开销似乎主要由分配堆栈帧的成本组成。