最近我在学习python的时候也遇到了同样的问题,碰巧看到了这个提问,还是挺有缘分的。
先说结论:直接使用lambda表达式时filter(lambdax:x%n>0, it),n会随着代码的运行而改变,导致filter函数在运行时出现错误。
具体分析过程:
1、本来想直接看filter函数的源码,但是因为自己是新手,一直找不到位置,而且看到有人讲有些内置函数是使用c语言来编写的,遂放弃。
2、开始自己写测试代码,思路很简单,就是看使用lambda表达式的时候,filter函数如何调用的。
首先看正确的情况下:
def test():
mylist=(x for x in range(2,10))
while True:
n=next(mylist)
#fnc=lambda x: x % n > 0
def fnc(x,n=n):
print("filter调用方法%d/%d结果为%s"%(x,n,x%n>0))
return x%n>0
yield n
mylist=filter(fnc,mylist)
for m in test():
print("-----遍历到%s"%m)
结果为:正常调用的结果,最后出现异常是因为生成器内容结束
不正常情况的代码就改了一行:def fnc(x,n=n):改为def fnc(x):
结果为:
可以很清楚的对比到:不正常的情况下,filter方法调用的表达式x % n > 0时,n都为一个值,而正常情况下则是调用的不同值。
其原因正是因为上文所提到的:直接使用lambda表达式时filter(lambdax,x%n>0, it),n会随着代码的运行而改变,导致filter函数在运行时出现错误。而使用filter(lambdax,n=n:x%n>0, it)时,n是复制一份到了filter方法中保存起来,后面n的改变不会修改此时的值。
3、为什么会有这样的结果呢?
首先通过python的help函数来查看filter的描述:
看起来有点不太明白,没关系,搜索一下中文版:filter(function,iterable)用 iterable 中函数 function 返回真的那些元素,构建一个新的迭代器。iterable 可以是一个序列,一个支持迭代的容器,或一个迭代器。如果 function 是 None ,则会假设它是一个身份函数,即 iterable 中所有返回假的元素会被移除。
请注意, filter(function, iterable) 相当于一个生成器表达式,当 function 不是 None 的时候为 (item for item in iterable if function(item));function 是 None 的时候为 (item for item in iterable if item) 。
filter函数的返回值是一个可迭代对象
filter在实现时采用了迭代器技术,将计算延迟到对filter函数返回结果进行遍历时才进行。就是filter函数返回的是一个迭代器,不是列表,在使用的时候才会计算,所以才会出现上面运行所得出的:调用值时才会计算,比如遍历到2之后再继续调用生成器,才会计算3%2是否有余数,返回true证明3可以,即输出3。注意后面的过滤过程中会调用每个n值不同的方法,具体实现没有源码我也不太懂,也没有搜索到相应的讲解。
4、回到廖雪峰的python教程:
在本章的函数式编程-返回函数一节中有明确提到: