惰性求值和lambda表达式的强大组合-Python高级技巧 (惰性求值和逻辑短路)
Lambda 表达式
在 Python 中,Lambda 表达式是一个匿名函数,它可以在需要函数对象的地方使用。Lambda 表达式的语法如下:
lambda arguments: expression
其中,
arguments
是参数列表,可以是 0 个或多个参数,用逗号分隔;
expression
是函数体,是一个表达式,返回值即为 Lambda 函数的返回值。
Lambda 表达式通常用于需要一个简单函数作为参数的场合,比如
map
、
filter
和
reduce
等高阶函数,以及排序、回调函数等场合。
例如,对一个列表进行平方操作:
numbers = [1, 2, 3, 4, 5] squared_numbers = list(map(lambda x: x2, numbers)) print(squared_numbers) 输出 [1, 4, 9, 16, 25]
Lambda 表达式还可以与条件表达式结合使用,实现简单的条件判断。例如,返回两个数中的最大值:
max_value = lambda a, b: a if a > b else b print(max_value(3, 5)) 输出 5
需要注意的是,Lambda 表达式只能包含一个表达式,而不能包含多条语句或复杂的控制流程。因此,Lambda 表达式通常用于编写简单的函数,不适用于复杂的业务逻辑。如果需要编写更复杂的函数,应该使用
def
语句定义普通的函数。
惰性求值
惰性求值(Lazy Evaluation)是一种编程策略,它延迟计算表达式的值直到真正需要时。在惰性求值中,表达式不会立即求值,而是在需要结果时才进行计算。
惰性求值的主要优势在于它可以节省计算资源并提高性能。当存在大量的计算或者有可能产生无用的计算结果时,惰性求值可以避免不必要的计算开销。通过只计算必要的部分,可以减少时间和空间的消耗。
惰性求值在很多编程语言中都有应用,例如函数式编程语言如 Haskell 和 Scala。在这些语言中,一些常见的数据结构和操作符都是惰性求值的,例如列表、流(Stream)和生成器(Generator)等。
下面是一个简单的示例,展示了惰性求值的概念:
def generate_numbers(): num = 1 while True: yield num num += 1 numbers = generate_numbers() 生成一个惰性序列 filtered_numbers = filter(lambda x: x % 2 == 0, numbers) 过滤出偶数 只有在需要结果时,才会进行计算 print(next(filtered_numbers)) 输出 2 print(next(filtered_numbers)) 输出 4 print(next(filtered_numbers)) 输出 6
在上述代码中,
generate_numbers()
函数返回一个生成器对象,它是一个惰性序列。而
filter()
函数通过传入一个 Lambda 表达式和生成器对象来创建另一个惰性序列,其中只包含偶数。通过调用
next()
函数,我们按需获取结果,每次只计算一个偶数。
需要注意的是,惰性求值并不适用于所有情况。有些场景下,我们需要立即获得全部结果,而不是按需计算。这就要根据具体的业务需求来选择适合的求值策略。
Lambda 表达式有何用处?如何使用
简单来说,编程中提到的 lambda 表达式,通常是在需要一个函数,但是又不想费神去命名一个函数的场合下使用,也就是指匿名函数。 这一用法跟所谓 λ 演算(题目说明里的维基链接)的关系,有点像原子弹和质能方程的关系,差别其实还是挺大的。 不谈形式化的 λ 演算,只说有实际用途的匿名函数。 先举一个普通的 Python 例子:将一个 list 里的每个元素都平方:map( lambda x: x*x, [y for y in range(10)] )这个写法要好过def sq(x): return x * xmap(sq, [y for y in range(10)]),因为后者多定义了一个(污染环境的)函数,尤其如果这个函数只会使用一次的话。 而且第一种写法实际上更易读,因为那个映射到列表上的函数具体是要做什么,非常一目了然。 如果你仔细观察自己的代码,会发现这种场景其实很常见:你在某处就真的只需要一个能做一件事情的函数而已,连它叫什么名字都无关紧要。 Lambda 表达式就可以用来做这件事。 进一步讲,匿名函数本质上就是一个函数,它所抽象出来的东西是一组运算。 这是什么意思呢?类比a = [1, 2, 3]和f = lambda x : x + 1,你会发现,等号右边的东西完全可以脱离等号左边的东西而存在,等号左边的名字只是右边之实体的标识符。 如果你能习惯 [1, 2, 3] 单独存在,那么 lambda x : x + 1 也能单独存在其实也就不难理解了,它的意义就是给「某个数加一」这一运算本身。 现在回头来看 map() 函数,它可以将一个函数映射到一个可枚举类型上面。 沿用上面给出的 a 和 f,可以写:map(f, a)也就是将函数 f 依次套用在 a 的每一个元素上面,获得结果 [2, 3, 4]。 现在用 lambda 表达式来替换 f,就变成:map( lambda x : x + 1, [1, 2, 3] )会不会觉得现在很一目了然了?尤其是类比a = [1, 2, 3]r = []for each in a: (each+1)这样的写法时,你会发现自己如果能将「遍历列表,给遇到的每个元素都做某种运算」的过程从一个循环里抽象出来成为一个函数 map,然后用 lambda 表达式将这种运算作为参数传给 map 的话,考虑事情的思维层级会高出一些来,需要顾及的细节也少了一点。 Python 之中,类似能用到 lambda 表达式的「高级」函数还有 reduce、filter 等等,很多语言也都有这样的工具(不过这些特性最好不要在 Python 中用太多。 这种能够接受一个函数作为参数的函数叫做「高阶函数」(higher-order function),是来自函数式编程(functional programming)的思想。 和其他很多语言相比,Python 的 lambda 限制多多,最严重的当属它只能由一条表达式组成。 这个限制主要是为了防止滥用,因为当人们发觉 lambda 很方便,就比较容易滥用,可是用多了会让程序看起来不那么清晰,毕竟每个人对于抽象层级的忍耐 / 理解程度都有所不同。
Python 中lambda和zip组合使用报错
我是网上copy别个感知器代码,我是py3.6也出现了这个问题。 PY2和py3在map和zip的地方有区别,py3的结果是迭代器,所以需要自己手动将结果转化为列表。 因此只是光改代码提示错误的地方还不够,出现map和zip的地方都需要修改。 更改主要两处代码(reduce(lambda a,b: a + b,list(map(lambda x: x[0] * x[1], list(zip(input_vec, )))))+) 2:def _update_weights(self, input_vec, output, label, rate):delta = label - = list(map(lambda x: x[1] + rate * delta * x[0],list(zip(input_vec, ))))# 更新 += rate * delta
免责声明:本文转载或采集自网络,版权归原作者所有。本网站刊发此文旨在传递更多信息,并不代表本网赞同其观点和对其真实性负责。如涉及版权、内容等问题,请联系本网,我们将在第一时间删除。同时,本网站不对所刊发内容的准确性、真实性、完整性、及时性、原创性等进行保证,请读者仅作参考,并请自行核实相关内容。对于因使用或依赖本文内容所产生的任何直接或间接损失,本网站不承担任何责任。