filter和sorted是经典的Python高阶函数。他们都是通过将函数名作为参数实现高阶的函数调用,达到筛选和排序的目的。
filter函数
filter函数是Python的内建函数,用于过滤序列。
它接收一个函数和一个序列。
把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。
这里的重点在于,作为参数的函数,必须返回的是一个布尔型的值(或者是0或非0的数值)。
举例说明:
假设有如下需求:在一个list中,删掉偶数,只保留奇数
此时,可以先建立一个函数is_odd,判断是不是一个奇数。
def is_odd(n):
return n % 2 == 1
接下来就可以构建一个filter函数如下:
filter(is_odd,[1,2,4,5,6,9,10,15])
filter返回的是一个filter对象,是一个Iterator。需要使用list构造函数来强制转换为实际数据。
list(filter(is_odd,[1,2,4,5,6,9,10,15]))
#结果:[1,5,9,15]
这里要注意。is_odd函数,如果只是写return n % 2行不行呢?
答案是肯定的。因为返回0表示假值,其余为真。
再举一个例子
把一个序列中的空字符串删掉
首先,先构建一个函数not_empty,判断是不是空字符串
def not_empty(s):
return s and s.strip()
接下来构建一个filter函数如下:
list(filter(not_empty, ['A', '', 'B', None, 'C', ' ']))
# 结果: ['A', 'B', 'C']
再来一个例子:
用filter求素数
埃式筛法
首先,列出2开始的所有自然数,构造序列
取序列第一个数2,他一定是素数,筛掉2的倍数。
取新序列的第一个数3,它一定是素数,筛掉3的倍数。
取新序列的第一个数5,它一定是素数,筛掉5的倍数。
不断筛选,得到所有素数。
具体实现方法:
先用生成器构造从3开始的奇数序列:
def _odd_iter():
n = 1
while True:
n = n + 2
yield n
再定义筛选函数
_not_divisible(n):
return lambda x: x % n > 0
相当于
def _not_divisible(n):
def fun(x):
return x % n > 0
return fun
正常来说,一个筛选函数只能有一个参数,用来放置实际筛选的对象。
例如:is_odd(n),可以用来筛选一个序列中所有基数,写成filter(is_odd,L)。
这其中is_odd是以一个函数名形式作为实参放进来。
但是对于本例来说,筛选函数_not_divisible要在一个序列(假设是L = [1,2,3,4,5])中筛选出不能被n整除的元素。如果n是一个常数,比如5。
那大可以写成:
def _not_divisible(x):
return x % 5 > 0
这样一来,可以使用函数filter(_not_divisible, L)来将L中所有的不能被5整除的数过滤出来。
然而,本例中n是个变量,加上原本的x,就是两个变量。不能写成:
def _not_divisible(x, n):
return x % n > 0
这样写会让filter函数语法错误。导致n无法传递给filter函数。
因此,采用了返回函数的方式。利用内嵌函数fun(x),或者例子中提到的lambda,作为函数_not_divisible(n)的返回值,相当于将n的值传递进fun函数(lambda函数),而又使得fun函数(lambda函数)有一个参数x,符合fitler函数的第一个函数参数标准,用于筛选。这恰恰是函数式编程的精髓。
最后,定义一个生成器,不断返回下一个素数。
def primes():
yield 2
it = _odd_iter() # 初始序列
while True:
n = next(it) # 返回序列的第一个数
yield n
it = filter(_not_divisible(n), it) # 构造新序列
由于primes()也是一个无限序列,所以调用时需要设置一个退出循环的条件
# 打印1000以内的素数:
for n in primes():
if n < 1000:
print(n)
else:
break
关于返回函数的函数
举个例子:写一个筛选函数times(n),从一个列表L中删选出是n的倍数的元素。
注意,filter函数返回的也是一个算法,也就是一个Iterator。 list()可以将其转化为一个普通列表。 next和函数调用同样可以滞后输出。
应该构建一个筛选函数,可以返回一个带一个参数x的函数 利用参数n,完成对n的倍数的筛选。 并在filter函数中利用。
def times(n):
return lambda x: x % n == 0
#1.list用法
print(list(filter(times(5), [1, 2, 5, 10, 12])))
#2.next用法
mm = filter(times(5), [1, 2, 5, 12, 10])
print(next(mm))
print(next(mm))
#3.函数调用用法
mm = filter(times(5), [1, 2, 5, 12, 10])
for n in mm:
print(n)
再举个例子:
筛选回数
回数是指从左向右读和从右向左读都是一样的数,例如12321,909。请利用filter()筛选出回数:
算法提示:先变成字符串,再考查字符串与反字符串是否相等。回文条件。
代码如下:
# # -*- coding: utf-8 -*-
def is_palindrome(n):
s = str(n)
return s == s[::-1]
# 测试:
output = filter(is_palindrome, range(1, 1000))
print('1~1000:', list(output))
if list(filter(is_palindrome, range(1, 200))) == [1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 22, 33, 44, 55, 66, 77, 88, 99, 101, 111, 121, 131, 141, 151, 161, 171, 181, 191]:
print('测试成功!')
else:
print('测试失败!')
好啦。就讲到这里。
看懂了就点个赞吧。