Python 中的 lambda 和lambda 有啥区别

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python 中的 lambda 和lambda 有啥区别相关的知识,希望对你有一定的参考价值。

参考技术A Python的lambda里只能写一行啦、不能有statement只能有expression啦,这些还是小问题,真正的问题是Python对Closure的实现根本是有缺陷的。闭包的实现都是错误的,哪来的真正的匿名函数?
比如在Python2里这样的代码是没法运行的,

def counter():
count = 0
def inner():
count += 1
return count
return inner

c = counter()
print c()

Python告诉你一个UnboundLocalError,count为什么会unbound呢,因为closure没有正确地实现。什么是closure呢,closure是一个二元组:lambda(别管是有名字的还是没名字的),和这个lambda定义时的environment。而这个environment包含了lambda中的自由变量(比如这里的count),这样才把这个lambda『封闭』起来了,所以叫闭包。
我所理解的『真正的』的lambda是说:完整地支持higher-order function,即函数可以作为函数的参数,也可以作为函数的返回值,那怕引入了mutation。为了达到这一点,语言的实现需要正确地实现closure和lexical scope。而mutation和lexical scope是两个正交的概念,Python因为有mutation而没有完整实现lexical scope进而没有完整地支持first-order function,这就叫broken lambda。Python3里新加的nonlocal关键字就是为了解决closure的历史问题。
然而同样的代码在Racket/Scala/OCaml里却可以跑地欢快:

(define (counter)
(define count 0)
(define (inner)
(begin (set! count (add1 count))
count))
inner)

(define c (counter))
(c) ;1
(c) ;2
(c) ;3

def counter(): () => Int =
var count = 0
def inner() =
count += 1
count

inner


val c = counter()
println(c())
println(c())
println(c())

let counter () =
let count = ref 0 in
let inner () =
count := !count + 1;
!count
in inner
;;

let c = counter();;
print_int(c());
print_int(c());
print_int(c());

真正的lambda就是正确而完整地实现了lexical scope和closure的lambda。这就是python的lambda和『真正的』的lambda的区别。
当然Python并不是函数式语言,Python也从来没有自我标榜是函数式语言,当年lambda都是一个Lisp程序员给Python加的,而且据说当时Guido是强烈反对的……
BTW,lambda这个名字确实没什么神秘的

===
Update:
经灵剑提醒,由于Racket和Python中对于list comprehension的实现不同,list comprehension的例子是不太恰当的。Racket中的list comprehension经过宏展开后是递归的函数调用的形式,而类似的python代码可能是这样的:

map(lambda i: lambda n: i+n, range(10))[3](4)

这个时候Python的行为和Racket是一样的。但对于list comprehension而言,Python并不是函数式语言(again),同Haskell、Scala、Racket这些的实现是不同的,在comprehension的过程中并没有创建出各自包含i的闭包。
原:
比如这个Python代码:

fs = [(lambda n: i + n) for i in range(10)]
fs[3](4)

fs[3](4)应该是几呢?Python告诉你是13 = = 因为每一个lambda都share了相同的i。
同样的代码再看看Racket里呢:

(define fs
(for/list ([i (range 10)])
(λ (n) (+ i n))))
((fourth fs) 4)

Racket里正确地告诉你结果是7。

Python(lambda)中的匿名函数

【中文标题】Python(lambda)中的匿名函数【英文标题】:Anonymous function in Python(lambda) [duplicate] 【发布时间】:2020-04-25 11:13:20 【问题描述】:

我在看,但我不明白匿名函数和普通函数之间的区别。

python(lambda) 中的匿名函数:

triangle_area = lambda base, height: (base, height) / 2

普通函数:

def triangle_area(base, height):
     return (base, height) / 2

但是当我为我调用函数时是一样的,无论你创建函数的方式如何。

triangle_area(10,7)

我希望我能很好地解释自己。

感谢您的帮助。

【问题讨论】:

匿名函数主要是为了节省空间。如果你是用过一次,或者非常简单的话,最好把它做成一行然后搞定。 匿名函数实际上应该始终保持匿名,例如一个关键函数sorted(lst, key = lambda x: len(x)**2 或一个函数列表[lambda x: x, lambda x: x /2] 等等——还有一些其他的区别,你可以在一个lambda表达式中做所有你可以在常规def函数中做的事情 【参考方案1】:

理想情况下,您永远不会以第一种方式编写它; “命名 lambdas”实际上是 against the recommendations of PEP8:

始终使用 def 语句而不是将 lambda 表达式直接绑定到标识符的赋值语句。

是的:

def f(x): return 2*x

没有:

f = lambda x: 2*x

Lambdas 很有用,但是当您不需要为函数命名时:

map(lambda n: n * 2, [1, 2, 3])

这与以下内容基本相同:

def double(n):
    return n * 2

map(double, [1, 2, 3])

但前者更简洁。

【讨论】:

讨厌我的命名空间被简短易读的 PEP8 兼容名称污染;) 无论如何这都不是一个主要的缺点(实际上更多的是事后考虑)

以上是关于Python 中的 lambda 和lambda 有啥区别的主要内容,如果未能解决你的问题,请参考以下文章

Python中的匿名函数——lambda函数

Python中的lambda

关于Python中的lambda

Python(lambda)中的匿名函数

lambda 函数中的 python 和 python-jose 错误

关于Python中的lambda