在python中filter参数_廖雪峰filter函数教程中传入的函数参数问题?

不仅是作用域问题,本质上是闭包的延迟绑定特性(late binding)。

首先看廖大牛的程序,其求质数的原理很简单:

1、通过_odd_iter()内部函数获得奇数生成器。

2、对每个奇数,用比其小的所有奇数去尝试整除,如不能整除,则该奇数为质数。

重点在于第2步的实现,此处用到了一个多层嵌套的filter()函数。

为了看得更明白,我们给程序增加一些输出(详见注释)。

#产生从3开始的奇数生成器

def _odd_iter():

n = 1

while True:

n = n+2

yield n

#判断是否被n整除

def _not_divisible(ii, nn): #函数局部空间变量名改为与传递参数不同,以便区分(不是必须)

return lambda x: print('level%d:%d%%%d' %(ii, x, nn)) or (x % nn > 0)

def primes():

yield 2

it = _odd_iter()

i = 0 #filter层数

while True:

n = next(it)

i += 1

print('=' * 30)

yield n

print('_' * 20)

it = filter(_not_divisible(i, n), it) #对奇数生成器进行筛选

def main():

p = primes()

k = 0

while k < 5:

print('prime-%d:' % k, next(p))

k += 1

if __name__ == '__main__':

main()

以上程序的输出如下,我采用注释方式#给出了输出的详细解释。

prime-0: 2 #yield 2语句输出第一个质数

==============================

prime-1: 3 #第一次执行next(it)后,yield n语句第一次输出

____________________

level 1: 5%3 #第二次执行next(it)时,即第一次调用filter()函数

==============================

prime-2: 5 #yield n语句第二次输出

____________________

level 1: 7%3 #第三次执行next(it),即第二次调用filter()函数(第一层/共二层)

level 2: 7%5 #(第二层/共二层)

==============================

prime-3: 7 #yield n语句第三次输出

____________________

level 1: 9%3 #第三次执行next(it),即第三次调用filter()函数(第一层/共三层),触发过滤条件

level 1: 11%3 #第四次执行next(it),即第四次调用filter()函数(第一层/共三层)(紧接上一个next(),未重新定义it)

level 2: 11%5 #第四次执行next(it),即第四次调用filter()函数(第二层/共三层)

level 3: 11%7 #第四次执行next(it),即第四次调用filter()函数(第三层/共三层)

==============================

prime-4: 11 #yield n语句第四次输出

上面的重点是:

1、执行next(it)语句进行输出时,filter()构成的迭代器才会实现。

2、filter()函数采用了外部函数_not_divisible()作为过滤条件,因此:

(1)由于是外部函数,所以函数参数必须显示传递。

(2)由于是外部函数,其在filter定义时就已经实现,也即已经将primes()函数空间的变量i、n的值的引用传递给_not_divisible()的函数局部变量ii、nn。因此,filter()函数每层的i和n不同。

下面我们将_not_divisible()改为闭包(内嵌函数):

#产生从3开始的奇数生成器

def _odd_iter():

n = 1

while True:

n = n+2

yield n

def primes():

def _not_divisible():

return lambda x: print('level%d:%d%%%d' %(i, x, n)) or (x % n > 0)

yield 2

it = _odd_iter()

i = 0 #filter层数

while True:

n = next(it)

i += 1

print('=' * 30)

yield n

print('_' * 20)

it = filter(_not_divisible(), it) #对奇数生成器进行筛选

def main():

p = primes()

k = 0

while k < 5:

print('prime-%d:' % k, next(p))

k += 1

if __name__ == '__main__':

main()

其输出如下:

prime-0: 2

==============================

prime-1: 3

____________________

level 1: 5%3 #第二次执行next(it)时,即第一次调用filter()函数

==============================

prime-2: 5

____________________

level 2: 7%5 #第三次执行next(it)时,即第二次调用filter()函数(第一层/共二层)

level 2: 7%5 #第三次执行next(it)时,即第二次调用filter()函数(第二层/共二层)

==============================

prime-3: 7

____________________

level 3: 9%7 #第四次执行next(it)时,即第三次调用filter()函数(第一层/共三层)

level 3: 9%7 #第四次执行next(it)时,即第三次调用filter()函数(第二层/共三层)

level 3: 9%7 #第四次执行next(it)时,即第三次调用filter()函数(第三层/共三层)

==============================

prime-4: 9

结合上面_not_divisible()作为外部函数时的重点解释,注意其区别:

1、再次强调,执行next(it)语句进行输出时,filter()构成的迭代器才会实现。

2、由于filter()函数采用了闭包(内嵌函数)作为过滤条件:

(1)作为闭包,变量i、n直接从primes()函数局部空间继承(自由变量),而不是显示定义。

(2)闭包具有延迟绑定特性(closure lazy binding),也即到闭包实现时,才会给其内部的只有变量赋值。针对本例,执行next(it)语句时,filter()迭代器实现,同时其过滤条件_not_divisible()也才实现。因此,filter()函数每层的i和n都一样(都是执行next(it)时的最新值)。

作者采用的匿名函数lambda实际上也构成了一个闭包,其中的参数n作为自由变量,只有在执行next(it)时才会实现,因此与上面的闭包效果一样,无法达到去除合数的效果。

要避免闭包延迟绑定导致的函数参数误用,最简单的方式是采用默认参数:

it = filter(lambda x, y = n: x % y > 0, it)

除此以外,还可采用偏函数、生成器函数等方式避免闭包的延迟绑定。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值