小知识,大挑战!本文正在参与「程序员必备小知识」创作活动
在多数条件下我们是不需要使用生成器函数,并手动yeild返回值的。
Python为我们提供了一种名为生成器表达式的东西,用于快速的实现生成器。
语法形式如下:
(xxxx for i in range(10)) # 姑且使用10 表示一个可迭代的对象
如果我们把上面的range(10)当作传入的参数,它会相当于这样的生成器函数
def foo(it: Iterable):
for i in it:
yield i
生成器表达式仅仅是这种函数的语法糖而已,但也确实够实用。
因为大多数使用我们都会把操作提取成一些函数,然后分别对对象应用操作。也就是说生成器表示式相当于我们经常对集合做的所谓的map操作。这也是python的惯用法,而不是使用python内置的map函数。而且实际上这个表达式还可以做一个filter操作。具体操作是在表达式的for循环后,添加if语句用于筛选出符合条件的元素。 像下面这样可以使用,可以选出序列中的3的倍数
(i for i in range(100) if i%3 == 0)
生成器表达式还有个好用的点在于,单独需要可迭代对象做调用时,使用生成表达式可以省略括号。
这里我们就顺便完成以到leetcode的题目来演示。
第十四题,最长公共前缀。题意如题,求几个字符串的公共的最长前缀。
我们就从最朴素的想法出发,一位一位的比较,比到不一样了就得到了最长的公共前缀。那么很显然,实际上,最长的情况下,我们也仅仅只需要比较到最短的字符串耗尽,而最短的字符串的前面匹配成功的部分就时前缀。
那么我们的代码可以像下面这样写:
from itertools import takewhile
class Solution:
def longestCommonPrefix(self, strs: List[str]) -> str:
shortest_str = min(strs, key=len)
def predicate(index_and_char):
index, char = index_and_char
return all(str_[index] == char for str_ in strs)
return "".join(char for index, char in takewhile(predicate, enumerate(shortest_str)))
这里借助了itertools的takewhile函数,因为如果使用if后面的不匹配也会做比较,而且如果匹配失败之后的位置又有匹配成功的字符,会有不必要的麻烦。
在all函数和join函数中我们也利用了先前说的语法糖。
与循环对比我个人还是更倾向这种生成表达式的做法,思路更清晰。 最后总结一些,一般来讲,在对元素的操作可以比较好的提取成函数操作时,推荐使用生成器表达式。
而当不太好抽取时使用生成器函数。比如上次说的斐波拉契数列,更适合使用函数。当然也不是不能使用表达式,只是需要一些奇怪的操作,比如我们借助海象运算符,可以像下面这样实现:
f0 = 1
f1 = 1
( (f0:=f1-f0,f1:=f1+f0) for i in range(10) )
只是会失去生成器表达式本应拥有的简洁性。