这个Python Lambda递归表达式是如何工作的?
rec_fn = lambda: 10==11 or rec_fn()
rec_fn()
我刚接触Python,想弄明白lambda表达式是怎么回事。有人能解释一下这个递归是怎么工作的吗?我能理解10==11是'假',所以rec_fn会一次又一次地被调用。
但我搞不懂的是这种看起来新奇的写法,lambda表达式是怎么写的。
以前的写法lambda x: x+y
是怎么了?这里有个参数'x'传入这个没有名字的函数里。
还有,为什么是
rec_fn() = lambda: .... // a syntax error
rec_fn = lambda: .... //syntactically correct - WHY?
rec_fn是什么?它是一个函数还是一个变量?
5 个回答
rec_fn = lambda: 10==11 or rec_fn()
rec_fn()
一个函数的内容在执行之前是不会被计算的。一开始,rec_fn
是没有值的。这个lambda表达式里面只是一些对rec_fn
这个变量的函数调用的表达式。因为函数还没被执行,所以不会出错。接着,这个新的lambda函数被赋值给变量rec_fn
,然后调用这个函数。现在因为函数正在被执行,它会执行到函数调用的那一步。这个表达式是10==11 or rec_fn()
。这是一个or
表达式,所以先计算左边的部分。10==11
是假的,所以必须计算右边的部分,也就是调用某个函数(或者其他可调用的对象)rec_fn
。在这个时候,rec_fn
被赋值为我们刚刚创建的函数(它自己),所以它会被递归调用。如此循环下去。这个过程可以理解为:
def rec_fn():
return 10==11 or rec_fn()
lambda可以使用任意数量的参数。在lambda: ...
的情况下,没有指定任何参数,所以它是一个“没有参数的函数”。
记住,函数(包括lambda)都是一等公民。你可以像对待其他对象一样传递它们,也可以把它们存储到其他变量中。
rec_fn = lambda: ...
这样是可以的,因为你定义了一个lambda函数并把它存储在变量rec_fn
中。你可以像调用其他函数一样,通过这个名字rec_fn()
来调用它。
rec_fn() = lambda: ...
而这样就会失败,因为你不能把任何东西赋值给函数调用rec_fn()
的结果。
以这种方式定义函数和正常的函数定义是很不同的:
def rec_fn2(): # here you may use the parens with the name to indicate it takes no arguments
... # unlike in the lambda assignment above
只要记住这个区别就可以了。
这是一个可能更容易理解的递归例子,使用了阶乘的lambda版本:
fact = lambda x: 1 if x == 0 else x*fact(x-1)
prin(fact(10))
输出结果:
3628800
不过要注意,Python有递归的限制哦。
这是使用或的例子,类似于if..else的写法:
print 1 or 'a'
print 'a' or False
print False or True or 0/9
fact_or = lambda x: x == 0 and 1 or x * fact_or(x-1)
print fact_or(10)
可以把函数调用想象成一个操作符。因为它确实是这样。当你写 rec_fn()
的时候,其实你在做两件事。首先,你在获取一个名为 rec_fn
的对象的引用。这个对象恰好是一个函数,但这并不重要(在Python中,除了函数之外的对象也可以被调用)。然后有 ()
,它的意思是“调用我刚才提到的那个对象”。你可以在不调用函数的情况下获取它的引用,只需省略括号,然后你可以给它起不同的名字,任何一个名字都可以通过加上括号来调用它。
def func1():
print "func1"
func2 = func1
func2() # prints "func1"
现在你可以看到lambda是怎么工作的。
func3 = lambda x: x+1
你做的事情和上面 func2 = func1
的那一行是一样的,只不过lambda表达式就是那个函数。语法只是不同;lambda函数可以在不命名的情况下定义。
lambda可以有任意数量的参数,所以 lambda: 3
是一个不接受任何参数并且总是返回3的函数,而 lambda x, y: x+y
是一个接受两个参数并返回它们和的函数。
至于 or
的用法,它利用了短路的特性。基本上,or
知道如果它的第一个操作数是 True
,那么就不需要计算第二个,因为结果无论第二个参数是什么都会是 True
。所以你可以把它理解为 if not 10==11: rec_fn()
。顺便提一下,and
也有短路的特性,不过它是在第一个参数是 False
的时候短路,因为它知道无论第二个参数是什么,结果都会是 False
。