Self-study Python Fish-C Note13 P48to49

avatar
作者
筋斗云
阅读量:0

函数 (part 3)

本节主要讲函数的 lambda 表达式, 生成器

lambda 表达式 (匿名函数)(P48)

匿名函数(lambda 表达式)语法规则:lambda arg1, arg2, arg3, ... argN : expression
其中 arg 为参数,expression 为函数实现的表达式以及返回值
我们把lambda表达式作为极致精简的函数,相当于传统函数定义的:
def <lambda> (arg1,arg2,arg3,... argN): return expression
示例 1:

# 传统定义的函数 def squareX(x):     return x*x squareX(3) 
9 
# lambda 表达式 squareY = lambda y : y*y squareY(3) 
9 
# 传统定义的函数函数名就是一个函数的引用 squareX 
<function __main__.squareX(x)> 
# 而 lambda 表达式,整个表达式就是一个函数的引用 squareY 
<function __main__.<lambda>(y)> 

两者重大的区别就是,lambda 是一个表达式,可以用在常规函数不可能存在的地方。
示例2:lambda 表达放入列表(这里只是做示例示范可以,但不推荐大家开发的时候这么写哈)

y = [lambda x:x*x,2,3] y[0](y[1]) # 把 y 列表的第二个值(2),传入列表第一个值 lambda 表达式中 
4 

lambda 表达式与 mapfilter 等函数合用。这两者的参数都是要求传入一个用于计算的函数,此时就可以使用 lambda 表达式。
比如 map 函数,第一个参数就是传入一个函数的引用,第二个参数要求是序列类型,其会把每个元素挨个传递给第一个参数指定的函数,在运算之后把结果构成一个迭代器返回。
示例3:

mapped = map(lambda x:ord(x)+10, 'abcdef') list(mapped) 
[107, 108, 109, 110, 111, 112] 
# 如果我们用传统的函数 def tradation(x):     return ord(x)+10 list(map(tradation,'abcdef')) 
[107, 108, 109, 110, 111, 112] 
# 再比如我们用在 filter 上 list(filter(lambda x : x%2,range(10))) # 求 10 以内的所有奇数 
[1, 3, 5, 7, 9] 

总结,lambda 是一个表达式而非语句,所以它能够出现在 Python 语法不允许 def 语句出现的地方(优势)。但由于所有的功能代码都局限在一个表达式中实现,因此 lambda 通常也只能实现那些较为简单的需求。一般来讲,匿名函数用于实现简单的需求,def 语句负责用于定义功能复杂的函数,去处理复杂的工作。

生成器 (generator) (P49)

对于调用一个普通的 Python 函数来说,一般我们都是从函数的第一行代码开始执行的,当所有语句执行完毕,或者遇到 return 语句的时候,这个函数就算是结束了。一旦函数将控制权交还给调用者的时候,这就意味这这个函数的结束。函数中做的所有工作,以及保存在局部变量中的数据也都会丢失。当再次调用的时候,一切从头开始。
有没有办法让函数在退出之后还能保留状态呢?使用闭包或者全局变量。不过过多使用全局变量会污染命名空间,闭包的定义又相对复杂。
此时,生成器 (generator) 就是一个更简单和安全的方法。
定义一个生成器:

在函数中使用 yield 表达式来代替 return语句

示例1:

def counter():     i=0     while i <= 3:         yield i         i+=1 # 这样我们就定义了一个 叫 counter 的生成器 # 现在我们调用 counter() 函数 counter() # 可以看到得到的不再是一个返回值,而是一个生成器对象 `generator object` 
<generator object counter at 0x0000017F4A3B8430> 
# counter 现在是一个生成器了,我们可以在 for 中使用 for i in counter():     print(i) 
0 1 2 3 

我们知道 for 语句,是从一个可迭代对象里面每次获取一个数据。这个 counter 生成器的作用就是每次调用的时候提供一个数据,注意是提供一个数据。
更深入讲,每次在执行到 yield i的时候,就生成一个数据,暂停并保留状态。下一次调用则从下一个语句 i+=1开始继续执行。
注意:生成器它不像是列表,元组之类的可迭代对象,生成器可以看作是一个制作机器,他的作用就是每调用一次提供一个数据,并且会记住当时的状态。而列表元组这些可迭代对象则是容器,他们里面存放的是早已经准备好的所有数据。生成器是一种特殊的迭代器,他首先不走回头路,其次支持 next()函数

c = counter() next(c) 
0 
next(c) 
1 
next(c) 
2 
next(c) # 如果再 使用 next(c) 就会抛出 StopIteration 的异常 
3 

由于生成器每调用一次获取一个结果的特性,所以生成器对象是无法使用下标索引这种随机访问的方式的

c = counter() print(next(c)) print(next(c)) # 此时 c 的状态 i =1 print('_'*30) for i in c: # 再调用是从 i=1开始 i=i+1     print(i) 
0 1 ______________________________ 2 3 

示例2:
使用生成器来实现 斐波那契数列
斐波那契数列:由 0 和 1 开始,之后的斐波那契数就是由之前的两数相加而得出:0,1,1,2,3,5,8 …

def fib():     back1,back2 = 0,1     while True:         yield back1         back1,back2 = back2,back1+back2          f = fib() print(next(f)) print(next(f)) print(next(f)) print(next(f)) print(next(f)) print(next(f)) print(next(f)) print(next(f)) 
0 1 1 2 3 5 8 13 

这里我们写了一个无限的斐波那契数列,如果我们不小心用到了 for 上,他就会一直生成下去。此时要用 control+C 强制退出,否则会一直进行下去

生成器表达式 (generator expression)

在之前的课程中我们提到过,列表有列表推导是而元组则没有。这里就可以做一个解答,列表推导式的 [] 换成 (),其实就变成了生成器表达式。
示例:

(i**2 for i in range(3)) 
<generator object <genexpr> at 0x0000017F4A3C49E0> 
t = (i**2 for i in range(3)) # range(3) 包括 0,1,2 不包括 3 print(next(t)) print(next(t)) print(next(t)) 
0 1 4 

这种利用推导的形式获得生成器的方法,我们称之为生成器表达式。生成器表达式和列表推导式最大的不同就是,列表推导式会一下子将所有数据生产出来并放到一个列表中,但是生成器表达式一次只生成一个数据。
总结来说,生成生成器的两种方法一个就是使用 yield 表达式,另一个就是直接使用生成器表达式。




附言: 题目:Self-study Python Fish-C Note-13 P48-P49 本文为自学B站上鱼C的python课程随手做的笔记。一些概念和例子我个人为更好的理解做了些查询和补充 因本人水平有限,如有任何问题,欢迎大家批评指正! 原视频链接:https://www.bilibili.com/video/BV1c4411e77t?p=8

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!