【python高级用法】匿名函数(lambda)、map()、filter()、reduce()系列高阶函数

前言

高阶函数是指能够接受其他函数作为参数或者返回函数作为结果的函数。在函数式编程中,高阶函数是一种强大的工具,它使得代码更加灵活、抽象和可复用。

 匿名函数

匿名函数是指在定义时不显式命名的函数,通常用于一次性的、简单的操作。在函数式编程中,匿名函数也被称为lambda表达式。

关键字lambda表示匿名函数,冒号前面的x表示函数参数

匿名函数有个限制,就是只能有一个表达式,不用写return,返回值就是该表达式的结果。

用匿名函数有个好处,因为函数没有名字,不必担心函数名冲突。此外,匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量来调用该函数

lambda parameters: expression

简单示例

add = lambda x, y: x + y
result = add(3, 5)
print(result)  # 输出 8

 把匿名函数作为返回值返回

def build(x, y):
    return lambda: x * x + y * y

 Map函数

 map()函数接收两个参数,一个是函数,一个是Iterablemap将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回。

def f(x):
    return x * x

r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9])
list(r)

map()传入的第一个参数是f,即函数对象本身。由于结果r是一个IteratorIterator惰性序列,因此通过list()函数让它把整个序列都计算出来并返回一个list

可迭代对象(Iterable):

  • 可迭代对象是指实现了__iter__()方法的对象,或者实现了__getitem__()方法的对象。
  • 可迭代对象可以被用于构建迭代器,它是一个容器,可以通过迭代获取其中的元素。
  • 示例:列表、元组、字符串等都是可迭代对象。
my_list = [1, 2, 3]
for item in my_list:
    print(item)

迭代器(Iterator):

  • 迭代器是可迭代对象的一种实现方式,它不仅实现了__iter__()方法,还实现了__next__()方法。
  • 迭代器通过__next__()方法逐一返回可迭代对象中的元素,当没有元素可返回时,抛出StopIteration异常。
  • 示例:使用iter()函数可以将可迭代对象转换为迭代器。
my_iter = iter(my_list)
print(next(my_iter))  # 输出 1
print(next(my_iter))  # 输出 2

总结区别:

  • 可迭代对象是具有__iter__()方法的对象,而迭代器是同时实现了__iter__()__next__()方法的对象。
  • 可迭代对象可以通过iter()函数转换为迭代器,然后使next()函数逐一获取元素
  • 迭代器是一种实现了延迟计算的机制,只在需要时才生成下一个元素,节省内存

在实际编程中,很多情况下我们使用for循环来迭代,而不直接操作迭代器。Python中的for循环实际上在内部调用了iter()函数,将可迭代对象转换为迭代器,并使用next()方法逐一获取元素。

 Reduce函数

 reduce把一个函数作用在一个序列[x1, x2, x3, ...]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算

reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)

 对一个序列求和

from functools import reduce
def add(x, y):
    return x + y

reduce(add, [1, 3, 5, 7, 9])

 把序列[1, 3, 5, 7, 9]变换成整数13579

from functools import reduce
def fn(x, y):
    return x * 10 + y

reduce(fn, [1, 3, 5, 7, 9])

 map和reduce搭配使用

str转换为int的函数

from functools import reduce
def fn(x, y):
    return x * 10 + y

def char2num(s):
    digits = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}
    return digits[s]

reduce(fn, map(char2num, '13579'))

 整合版

from functools import reduce

DIGITS = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}

def str2int(s):
    def fn(x, y):
        return x * 10 + y
    def char2num(s):
        return DIGITS[s]
    return reduce(fn, map(char2num, s))

map,reduce和匿名函数的组合使用

from functools import reduce

DIGITS = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}

def char2num(s):
    return DIGITS[s]

def str2int(s):
    return reduce(lambda x, y: x * 10 + y, map(char2num, s))

 Filter函数

 filter()也接收一个函数和一个序列。map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。  

 在一个list中,删掉偶数,只保留奇数

def is_odd(n):
    return n % 2 == 1

list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15]))
# 结果: [1, 5, 9, 15]

 把一个序列中的空字符串删掉

def not_empty(s):
    return s and s.strip()

list(filter(not_empty, ['A', '', 'B', None, 'C', '  ']))
# 结果: ['A', 'B', 'C']

可见用filter()这个高阶函数,关键在于正确实现一个“筛选”函数

注意到filter()函数返回的是一个Iterator,也就是一个惰性序列,所以要强迫filter()完成计算结果,需要用list()函数获得所有结果并返回list。

 

用filter求素数

计算素数的一个方法是埃氏筛法,它的算法理解起来非常简单:

首先,列出从2开始的所有自然数,构造一个序列:

2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...

取序列的第一个数2,它一定是素数,然后用2把序列的2的倍数筛掉:

3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...

取新序列的第一个数3,它一定是素数,然后用3把序列的3的倍数筛掉:

5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...

取新序列的第一个数5,然后用5把序列的5的倍数筛掉:

7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...

不断筛下去,就可以得到所有的素数。

先构造一个从3开始的奇数序列

def _odd_iter():
    n = 1
    while True:
        n = n + 2
        yield n

注意这是一个生成器,并且是一个无限序列。

然后定义一个筛选函数

def _not_divisible(n):
    return lambda x: x % n > 0

 最后,定义一个生成器,不断返回下一个素数

def primes():
    yield 2
    it = _odd_iter() # 初始序列
    while True:
        n = next(it) # 返回序列的第一个数
        yield n
        it = filter(_not_divisible(n), it) # 构造新序列

 这个生成器先返回第一个素数2,然后,利用filter()不断产生筛选后的新的序列。

 由于primes()也是一个无限序列,所以调用时需要设置一个退出循环的条件:

# 打印1000以内的素数:
for n in primes():
    if n < 1000:
        print(n)
    else:
        break

 注意到Iterator是惰性计算的序列,所以我们可以用Python表示“全体自然数”,“全体素数”这样的序列,而代码非常简洁

迭代器(Iterator):

  • 迭代器是一个具有__iter__()__next__()方法的对象。
  • __iter__()返回迭代器对象本身,而__next__()用于获取迭代器中的下一个元素。
  • 当没有元素可返回时,__next__()方法应该抛出StopIteration异常,通知迭代已经结束。
  • 示例:使用iter()函数可以将可迭代对象转换为迭代器,并使用next()函数逐一获取元素。
my_iter = iter([1, 2, 3])
print(next(my_iter))  # 输出 1
print(next(my_iter))  # 输出 2

生成器(Generator):

  • 生成器是一种特殊的迭代器,可以通过函数和yield关键字创建。
  • 函数中包含yield语句的时候,它不是普通的函数,而是一个生成器函数。
  • 生成器函数每次调用yield时,会暂停执行并将结果返回给调用者,保持函数状态,等待下一次调用。
  • 示例:使用生成器函数创建生成器。
def my_generator():
    yield 1
    yield 2
    yield 3

gen = my_generator()
print(next(gen))  # 输出 1
print(next(gen))  # 输出 2

区别

  • 迭代器是一种实现了__iter__()__next__()方法的对象,可以通过iter()函数和next()函数进行迭代。
  • 生成器是一种特殊的迭代器,通过生成器函数和yield语句实现,更方便、简洁地创建迭代器。
  • 生成器具有惰性计算特性,只在需要时才生成下一个元素,节省内存。
  • 迭代器和生成器都是用于处理可迭代对象的工具,使得在处理大数据集时能够更有效地进行迭代操作。

 参考链接:

MapReduce: Simplified Data Processing on Large Clusters

map/reduce - 廖雪峰的官方网站

你可能感兴趣的:(python,开发语言)