我正在试图弄清楚Python lambdas。lambda是那些在现实生活中应该被遗忘的“有趣”语言项目之一吗?
lambda
我确信有一些边缘情况可能需要它,但考虑到它的模糊性,它在未来版本中被重新定义的可能性(我的假设基于它的各种定义)以及编码清晰度的降低-应该避免吗?
这让我想起了C类型的溢出(缓冲区溢出)-指向顶部变量并重载以设置其他字段值。这感觉有点像技术表演但维护编码器的噩梦。
我不能说python对lambda的特殊实现,但总的来说lambda函数真的很方便。它们是函数式编程的核心技术(甚至可能是技术),它们在面向对象程序中也非常有用。对于某些类型的问题,它们是最好的解决方案,所以当然不应该忘记!
我建议你阅读关闭和map函数(链接到python文档,但它存在于几乎所有支持函数式构造的语言中),看看它为什么有用。
几乎任何你可以用lambda做的事情,你都可以用命名函数或列表和生成器表达式做得更好。
因此,在大多数情况下,您应该只是其中之一,基本上在任何情况下(除了在交互式解释器中编写的从头开始代码)。
我怀疑lambda会消失。请参阅吉多的帖子关于最终放弃尝试删除它。另请参阅冲突的概述。
您可以查看这篇文章,了解有关Python功能特性背后交易的更多历史:http://python-history.blogspot.com/2009/04/origins-of-pythons-functional-features.html
奇怪的是,最初推动引入lambda和其他功能特性的map、filter和duce函数在很大程度上已经被列表推导和生成器表达式所取代。事实上,在Python 3.0中,Reduce函数已从内置函数列表中删除。(但是,没有必要发送有关删除lambda、map或filter的投诉:它们正在保留。:-)
我自己的意见:就清晰度而言,lambda很少值得。通常有一个不包括lambda的更清晰的解决方案。
Lambdas通常与函数式编程风格密切相关。您可以通过将函数应用于某些数据并合并结果来解决问题的想法,是谷歌用来实现其大部分算法的想法。
以函数式编程风格编写的程序很容易并行化,因此在现代多核机器中变得越来越重要。所以,不,你不应该忘记他们。
首先恭喜你成功地找到了lambda。在我看来,这是一个非常强大的构造。如今函数式编程语言的趋势无疑表明,它既不应该避免,也不会在不久的将来被重新定义。
你只需要稍微改变一下想法。我相信很快你就会喜欢它。但是如果你只处理python要小心。因为lambda不是一个真正的闭包,它在某种程度上“坏了”:蟒蛇lambda坏了
我已经使用Python几年了,我从来没有遇到过需要 lambda的情况。真的,正如教程所说,这只是为了语法糖。
lambda是处理高阶函数的非常重要的抽象机制的一部分。要正确理解它的值,请观看艾贝尔森和苏斯曼的高质量课程,并阅读本书SICP
这些是现代软件业务中的相关问题,并且变得越来越流行。
你是在说lambda表达式吗?就像
lambda x: x**2 + 2*x - 5
这些东西实际上非常有用。Python支持一种名为函数式编程的编程风格,您可以将函数传递给其他函数来做事情。示例:
mult3 = filter(lambda x: x % 3 == 0, [1, 2, 3, 4, 5, 6, 7, 8, 9])
将mult3设置为[3, 6, 9],即原始列表中3的倍数。这比
mult3
[3, 6, 9]
def filterfunc(x):return x % 3 == 0mult3 = filter(filterfunc, [1, 2, 3, 4, 5, 6, 7, 8, 9])
当然,在这种情况下,你可以做与列表理解相同的事情:
mult3 = [x for x in [1, 2, 3, 4, 5, 6, 7, 8, 9] if x % 3 == 0]
(甚至作为range(3,10,3)),但还有许多其他更复杂的用例,您不能使用列表理解,而lambda函数可能是编写某些内容的最短方法。
range(3,10,3)
从另一个函数返回一个函数
>>> def transform(n):... return lambda x: x + n...>>> f = transform(3)>>> f(4)7
这通常用于创建函数包装器,例如Python的装饰器。
将可迭代序列的元素与reduce()组合
reduce()
>>> reduce(lambda a, b: '{}, {}'.format(a, b), [1, 2, 3, 4, 5, 6, 7, 8, 9])'1, 2, 3, 4, 5, 6, 7, 8, 9'
按备用键排序
>>> sorted([1, 2, 3, 4, 5, 6, 7, 8, 9], key=lambda x: abs(5-x))[5, 4, 6, 3, 7, 2, 8, 1, 9]
我经常使用lambda函数。我花了一段时间才习惯它们,但最终我明白它们是语言中非常有价值的一部分。
Lambda是一个过程构造函数。您可以在运行时合成程序,尽管Python的lambda不是很强大。请注意,很少有人理解这种编程。
我刚刚开始使用Python,并首先进入了Lambda——这花了我一段时间才弄清楚。
请注意,这不是对任何事情的谴责。每个人都有一套不容易得到的不同东西。
lambda是那些在现实生活中应该被遗忘的“有趣”语言项目之一吗?
不。
我相信在一些极端情况下可能需要它,但考虑到它的默默无闻,
它并不模糊。在我工作的过去2个团队中,每个人都一直在使用这个功能。
它在未来版本中被重新定义的可能性(我的假设基于它的各种定义)
除了几年前修复闭包语义学之外,我还没有看到在Python中重新定义它的严肃建议。
以及编码清晰度降低-应该避免吗?
如果你使用得当,它并不那么清晰。相反,拥有更多可用的语言结构增加清晰度。
这让我想起了C类型的溢出(缓冲区溢出)——指向顶部变量并重载以设置其他字段值……有点像技术表演,但维护编码器的噩梦。
Lambda就像缓冲区溢出?哇。如果你认为这是一个“维护噩梦”,我无法想象你是如何使用lambda的。
我经常使用它,主要是作为空对象或将参数部分绑定到函数。
以下是一些例子:
{DATA_PACKET: self.handle_data_packetsNET_PACKET: self.handle_hardware_packets}.get(packet_type, lambda x : None)(payload)
假设我有以下API
def dump_hex(file, var)# some codepass class X(object):#...def packet_received(data):# some kind of preprocessingself.callback(data)#...
然后,当我不想快速将收到的数据转储到一个文件时,我这样做:
dump_file = file('hex_dump.txt','w')X.callback = lambda (x): dump_hex(dump_file, x)...dump_file.close()
两行总结:
我今天开始读David Mertz的书《Python中的文本处理》。虽然他对Lambda的描述相当简洁,但第一章中的示例结合附录A中的解释让我对它们一目了然,突然之间我明白了它们的价值。这并不是说他的解释对你有用,我还在发现阶段,所以除了以下几点,我不会试图对这些回答进行补充:我是Python新手我是OOP新手Lambdas对我来说是一场斗争现在我读了Mertz,我认为我得到了它们,我认为它们非常有用,因为我认为它们允许更清洁的编程方法。
作为一个非OOP程序员,在阅读lambdas代码时(直到上周还在阅读列表理解),我有这样的想法——这很简单?。我今天终于意识到,实际上这些特性使代码更具可读性,也更容易理解,而不是其他选择——这总是某种循环。我还意识到,就像财务报表一样——Python不是为新手用户设计的,而是为想要接受教育的用户设计的。我不敢相信这种语言有多强大。当我(最终)意识到lambdas的目的和价值时,我想撕毁大约30个程序,开始在合适的地方重新放入lambdas。
在Python中,lambda只是一种内联定义函数的方式,
a = lambda x: x + 1print a(1)
和…
def a(x): return x + 1print a(1)
…确切相同。
你可以用lambda做任何你不能用常规函数做的事情——在Python中,函数是一个对象,就像其他任何东西一样,lambda只是定义一个函数:
>>> a = lambda x: x + 1>>> type(a)<type 'function'>
老实说,我认为lambda关键字在Python中是多余的-我从来没有需要使用它们(或者看到一个使用常规函数,列表理解或许多内置函数中的一个可以更好地使用的地方)
举一个完全随机的例子,来自文章“Python的lambda坏了!”:
要查看lambda是如何被破坏的,请尝试生成一个函数列表fs=[f0,...,f9],其中fi(n)=i+n。第一次尝试: >>> fs = [(lambda n: i + n) for i in range(10)]>>> fs[3](4)13
要查看lambda是如何被破坏的,请尝试生成一个函数列表fs=[f0,...,f9],其中fi(n)=i+n。第一次尝试:
fs=[f0,...,f9]
fi(n)=i+n
>>> fs = [(lambda n: i + n) for i in range(10)]>>> fs[3](4)13
我认为,即使这确实有效,它也是可怕的和“unpythonic”,同样的功能可以用无数其他方式编写,例如:
>>> n = 4>>> [i + n for i in range(10)][4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
是的,这不一样,但我已经从未看到了一个原因,需要在列表中生成一组lambda函数。这在其他语言中可能有意义,但Python不是Haskell(或Lisp,或…)
请注意,我们可以使用lambda并且仍然可以实现所需的结果如下: >>> fs = [(lambda n,i=i: i + n) for i in range(10)]>>> fs[3](4)7
请注意,我们可以使用lambda并且仍然可以实现所需的结果如下:
>>> fs = [(lambda n,i=i: i + n) for i in range(10)]>>> fs[3](4)7
编辑:
在某些情况下,lambda很有用,例如,在PyQt应用程序中连接信号时,它通常很方便,如下所示:
w = PyQt4.QtGui.QLineEdit()w.textChanged.connect(lambda event: dothing())
仅仅执行w.textChanged.connect(dothing)就会使用额外的event参数调用dothing方法并导致错误。使用lambda意味着我们可以整齐地删除参数而无需定义包装函数。
w.textChanged.connect(dothing)
event
dothing
lambda只是表示function的一种奇特方式。除了它的名字,它没有任何模糊、恐吓或神秘的东西。当你阅读以下行时,在你的脑海中将lambda替换为function:
function
>>> f = lambda x: x + 1>>> f(3)4
它只是定义了一个x的函数。其他一些语言,如R,明确地说:
x
R
> f = function(x) { x + 1 }> f(3)4
看到了吗?这是编程中最自然的事情之一。
在我看来,关于lambda的一个好处是被低估了,它是一种将简单表单的评估推迟到需要值的方式。让我解释一下。
许多库例程的实现都是为了允许某些参数是可调用的(lambda就是其中之一)。其思想是实际值仅在将要使用它的时间(而不是在调用它的时间)计算。一个(人为的)例子可能有助于说明这一点。假设你有一个例程要记录给定的时间戳。你希望例程使用当前时间减30分钟。你应该这样称呼它
log_timestamp(datetime.datetime.now() - datetime.timedelta(minutes = 30))
现在假设只有当某个事件发生时才会调用实际的函数,并且您希望仅在该时间计算时间戳。你可以这样做
log_timestamp(lambda : datetime.datetime.now() - datetime.timedelta(minutes = 30))
假设log_timestamp可以处理这样的可调用对象,它将在需要时评估它,您将在那个时候获得时间戳。
log_timestamp
当然还有其他方法可以做到这一点(例如使用operator模块),但我希望我已经传达了这一点。
operator
更新:这里是一个更具体的现实世界示例。
更新2:我认为这是所谓的Thunk的一个例子。
我发现lambda对于执行相同操作但适用于不同情况的函数列表很有用。
像Mozilla复数规则:
plural_rules = [lambda n: 'all',lambda n: 'singular' if n == 1 else 'plural',lambda n: 'singular' if 0 <= n <= 1 else 'plural',...]# Call plural rule #1 with argument 4 to find out which sentence form to use.plural_rule[1](4) # returns 'plural'
如果你必须为所有这些定义一个函数,到最后你会发疯的。此外,使用plural_rule_1、plural_rule_2等函数名称也不太好,当你依赖变量函数id时,你需要eval()它。
plural_rule_1
plural_rule_2
eval()
如上所述,Python中的lambda运算符定义了一个匿名函数,而Python中的函数是闭包。重要的是不要将闭包的概念与运算符lambda混淆,后者对它们来说只是语法美沙酮。
几年前,当我开始学习Python时,我经常使用lambdas,认为它们很酷,还有列表推导功能。然而,我写了一个用Python编写的大型网站,并且必须维护这个网站,大约有几千个功能点。我从经验中了解到,lambdas可能可以用原型来制作东西,但除了保存一些键-斯托克斯之外,它没有提供任何超过内联函数(命名闭包)的功能,有时甚至没有。
基本上这可以归结为几点:
这就有足够的理由将它们四舍五入并将它们转换为命名闭包。然而,我对匿名闭包还有另外两个不满。
第一个怨恨很简单,他们只是另一个不必要的关键字杂乱的语言。
第二个怨恨更深,在范式级别上,即我不喜欢他们提倡函数式编程风格,因为这种风格不如消息传递、面向对象或过程风格灵活,因为lambda演算不是图灵完备的(幸运的是,在Python中,即使在lambda中,我们仍然可以打破这种限制)。
有一个隐式的返回,即它们看起来“应该”是函数。
它们是另一种更明确、更可读、更可重用和更通用的机制:方法的替代状态隐藏机制。
我努力编写无lambda的Python,并立即删除lambda。我认为没有lambda的Python会稍微好一点,但这只是我的看法。
lambda在GUI编程中非常有用。例如,假设您正在创建一组按钮,并且您想使用单个参数化回调而不是每个按钮唯一的回调。Lambda让您轻松完成:
for value in ["one","two","three"]:b = tk.Button(label=value, command=lambda arg=value: my_callback(arg))b.pack()
(注意:虽然这个问题专门询问lambda,但你也可以使用functools.partial来获得相同类型的结果)
另一种方法是为每个按钮创建一个单独的回调,这可能会导致重复的代码。
我可以给你一个我真正需要lambda的例子。我正在制作一个图形程序,在这个程序中,用户右键单击一个文件并将其分配给三个选项之一。事实证明,在Tkinter(我正在编写的GUI界面程序)中,当有人按下按钮时,它不能分配给接受参数的命令。所以如果我选择了其中一个选项并希望我的选择结果是:
print 'hi there'
那没什么大不了的。但是如果我需要我的选择有一个特定的细节怎么办?例如,如果我选择选择A,它调用一个函数,该函数接受一些依赖于选择A、B或C的参数,TKinter无法支持这一点。Lamda实际上是解决这个问题的唯一选择…
使用lambda的一个有用案例是提高长列表理解的易读性。在这个例子中,loop_dic是清晰的缩写,但想象loop_dic很长。如果你只是使用一个包含i的普通值而不是该值的lambda版本,你会得到一个NameError。
loop_dic
i
NameError
>>> lis = [{"name": "Peter"}, {"name": "Josef"}] >>> loop_dic = lambda i: {"name": i["name"] + " Wallace" }>>> new_lis = [loop_dic(i) for i in lis] >>> new_lis[{'name': 'Peter Wallace'}, {'name': 'Josef Wallace'}]
而不是
>>> lis = [{"name": "Peter"}, {"name": "Josef"}] >>> new_lis = [{"name": i["name"] + " Wallace"} for i in lis] >>> new_lis[{'name': 'Peter Wallace'}, {'name': 'Josef Wallace'}]
Lambda实际上是源自函数式编程思想的非常强大的构造,在Python的不久的将来,它绝不会被轻易修改、重新定义或删除。它们帮助您编写更强大的代码,因为它允许您将函数作为参数传递,从而将函数视为一等公民的想法。
Lambdas确实容易令人困惑,但一旦获得了扎实的理解,您就可以编写像这样干净优雅的代码:
squared = map(lambda x: x*x, [1, 2, 3, 4, 5])
上面的代码行返回列表中数字的平方列表。当然,您也可以这样做:
def square(x):return x*x squared = map(square, [1, 2, 3, 4, 5])
很明显,前一个代码更短,如果您打算只在一个地方使用map函数(或任何将函数作为参数的类似函数),这尤其如此。这也使代码更加直观和优雅。
此外,正如@David Zaslavsky在他的回答中提到的,列表推导并不总是正确的方法,特别是如果你的列表必须从一些模糊的数学方法中获取值。
从更实际的角度来看,最近lambdas对我来说最大的优势之一是GUI和事件驱动编程。如果你看看Tkinter中的回调,它们作为参数的只是触发它们的事件。例如。
def define_bindings(widget):widget.bind("<Button-1>", do-something-cool) def do-something-cool(event):#Your code to execute on the event trigger
现在,如果你有一些参数要传递呢?像传递两个参数来存储鼠标单击的坐标一样简单。你可以像这样轻松地做到这一点:
def main():# define widgets and other imp stuffx, y = None, Nonewidget.bind("<Button-1>", lambda event: do-something-cool(x, y)) def do-something-cool(event, x, y):x = event.xy = event.y#Do other cool stuff
现在你可以争辩说这可以使用全局变量来完成,但是你真的想担心内存管理和泄漏吗?特别是如果全局变量只在一个特定的地方使用的话?那只是糟糕的编程风格。
简而言之,lambdas很棒,永远不应该被低估。Python lambdas与LISP lambdas不同(它们更强大),但你真的可以用它们做很多神奇的事情。
我使用lambda来避免代码重复。这将使函数易于理解例如:
def a_func()...if some_conditon:...call_some_big_func(arg1, arg2, arg3, arg4...)else...call_some_big_func(arg1, arg2, arg3, arg4...)
我用一个临时lambda代替它
def a_func()...call_big_f = lambda args_that_change: call_some_big_func(arg1, arg2, arg3, args_that_change)if some_conditon:...call_big_f(argX)else...call_big_f(argY)
Lambda函数是一种创建函数的非官僚方式。
就是这样。例如,假设你有你的main函数,需要平方值。让我们看看传统的方法和lambda的方法来执行此操作:
传统方式:
def main():......y = square(some_number)...return something def square(x):return x**2
lambda方式:
def main():...square = lambda x: x**2y = square(some_number)return something
看到区别了吗?
Lambda函数非常适合列表,例如列表理解或map。事实上,列表理解是一种使用lambda表达自己的“pythonic”方式。例如:
>>>a = [1,2,3,4]>>>[x**2 for x in a][1,4,9,16]
让我们看看语法的每个元素是什么意思:
[]:“给我一个列表” x**2:"使用这个新生函数" 对于a中的x:“进入a中的每个元素”
[]:“给我一个列表”
x**2:"使用这个新生函数"
对于a中的x:“进入a中的每个元素”
这很方便,呃?创建这样的函数。让我们使用lambda重写它:
>>> square = lambda x: x**2>>> [square(s) for x in a][1,4,9,16]
现在让我们使用map,它是一样的,但更与语言无关。地图需要2个参数:
(一)一个功能
(ii)可迭代
并为您提供一个列表,其中每个元素都是应用于可迭代对象的每个元素的函数。
因此,使用map我们将有:
>>> a = [1,2,3,4]>>> squared_list = map(lambda x: x**2, a)
如果你掌握了lambda和映射,你将有很大的能力以简洁的方式操作数据。Lambda函数既不晦涩,也不会影响代码的清晰度。不要将硬东西和新东西混淆。一旦你开始使用它们,你会发现它非常清晰。
我使用lambda来创建包含参数的回调。在一行中编写lambda比编写执行相同功能的方法更清晰。
例如:
import imported.module def func():return lambda: imported.module.method("foo", "bar")
而不是:
import imported.module def func():def cb():return imported.module.method("foo", "bar")return cb
我是一个Python初学者,所以为了清楚地了解lambda,我将其与'for'循环进行了比较;在效率方面。这是代码(python 2.7)-
import timestart = time.time() # Measure the time taken for execution def first():squares = map(lambda x: x**2, range(10))# ^ Lambdaend = time.time()elapsed = end - startprint elapsed + ' seconds'return elapsed # gives 0.0 seconds def second():lst = []for i in range(10):lst.append(i**2)# ^ a 'for' loopend = time.time()elapsed = end - startprint elapsed + ' seconds'return elapsed # gives 0.0019998550415 seconds. print abs(second() - first()) # Gives 0.0019998550415 seconds!(duh)