Python学习笔记 -- 函数式编程之高阶函数

函数式编程

函数式编程(Functional Programming),是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量。因此,任意一个函数,只要输入是确定的,输出就是确定的。

函数式编程是一种"编程范式"(programming paradigm)。它属于"结构化编程"的一种,主要思想是把运算过程尽量写成一系列嵌套的函数调用。

函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数!Python对函数式编程提供部分支持。由于Python允许使用变量,因此,Python不是纯函数式编程语言。

更多内容

  • 廖雪峰Python教程 函数式编程
  • 函数式编程初探 - 阮一峰
  • 函数式编程 - 看云
  • 函数式编程 - 百度百科

高阶函数

函数式编程中,可以将函数当作变量一样使用。接受函数为参数,或者把函数作为结果返回的函数称为高阶函数(Higher-order Functions)

def double(x):
    return 2 * x

def square(x):
    return x * x

def func(g, arr):
    return [g(x) for x in arr]

>>> arr1 = func(double, [1, 2, 3, 4])
>>> arr2 = func(square, [1, 2, 3, 4])
>>> arr1
[2, 4, 6, 8]
>>> arr2
[1, 4, 9, 16]

map()/reduce()/filter()/sorted() /apply() 是 Python 中较为常用的高阶函数,它们为函数式编程提供了不少便利。

Python 3 中,mapfilter 还是内置函数,但是由于引入了列表推导和生成器表达式,它们变得没那么重要了。列表推导或生成器表达式具有 mapfilter 两个函数的功能,而且更易于阅读。apply 函数在Python 2.3 中标记为过时,在Python 3 中移除了,因为不再需要它了。如果想使用不定量的参数调用函数,可以编写 fn(*args, **keywords),不用再编写 apply(fn, args,kwargs)


map()

map 函数将传入的函数依次作用到序列的每个元素,并把结果作为新的 Iterator 返回。map 函数语法:

# map(function, iterable, ...)	
## 参数 —— function : 函数 ;  iterable : 一个或多个序列
## 返回值 —— Python 2.x 返回列表; Python 3.x 返回迭代器(map)

def square(x):		# 计算平方数	
    return x ** 2

>>> map(square, [1,2,3,4,5])   # 计算列表各个元素的平方
<map object at 0x000001B9F81ADEB8>
>>> map(lambda x: x ** 2, [1, 2, 3, 4, 5])  # 使用 lambda 匿名函数
# [1, 4, 9, 16, 25]			  		 # python2输出结果
<map object at 0x000001B9F81ADEB8>    # python3输出结果(可使用list()函数对map函数返回结果进行转换)
>>> list(map(lambda x: x ** 2, [1, 2, 3, 4, 5]))  
[1, 4, 9, 16, 25]

# map实现list的值的格式批量转换
>>> list(map(str, [1, 2, 3, 4]))
['1', '2', '3', '4']
>>> list(map(int, ['1', '2', '3', '4']))
[1, 2, 3, 4]

# 提供了两个列表,对相同位置的列表数据进行相加
>>> map(lambda x, y: x + y, [1, 3, 5, 7, 9], [2, 4, 6, 8, 10])
[3, 7, 11, 15, 19]

# -------- 列表推导式实现上述功能 -----------
>>> [square(n) for n in [1,2,3,4,5]]
[1, 4, 9, 16, 25]
>>> [str(n) for n in [1,2,3,4]]
['1', '2', '3', '4']
>>> [int(n) for n in ['1', '2', '3', '4']]
[1, 2, 3, 4]

reduce()

reduce() 函数会对参数序列中元素进行累积。函数将一个数据集合(链表,元组等)中的所有数据进行下列操作:用传给 reduce() 中的函数 function(有两个参数)先对集合中的第 1、2 个元素进行操作,得到的结果后继续和序列的下一个元素做累积计算,直到累积到列表最后一个数据。Python 2 中,reduce 是内置函数,但是在Python 3 中放到functools 模块里了。reduce() 函数语法:

# reduce(function, iterable[, initializer])
## 参数 —— function:函数,有两个参数; iterable : 可迭代对象; initializer:可选,初始参数
## 返回值 —— 返回函数计算结果。

# 序列求和实现
from functools import reduce	# Python3 需要引入
def add(x, y) :            # 两数相加(或使用 from operator import add)
	return x + y

>>> reduce(add, [1,2,3,4,5])   # 计算列表和:1+2+3+4+5
15
>>> reduce(lambda x, y: x * y, [1, 2, 3, 4])  # 相当于 ((1 * 2) * 3) * 4
24
>>> reduce(lambda x, y: x * y, [1, 2, 3, 4], 5) # ((((5 * 1) * 2) * 3)) * 4
120
>>> reduce(lambda x, y: x / y, [2, 3, 4], 72)  #  (((72 / 2) / 3)) / 4
3
>>> reduce(lambda x, y: x + y, [1, 2, 3, 4], 5)  # ((((5 + 1) + 2) + 3)) + 4
15
>>> reduce(lambda x, y: x - y, [8, 5, 1], 20)  # ((20 - 8) - 5) - 1
6
>>> f = lambda a, b: a if (a > b) else b   # 两两比较,取最大值
>>> reduce(f, [5, 8, 1, 10])
10

** 注意:** 在 Python3 中,reduce() 函数已经被从全局名字空间里移除了,它现在被放置在 fucntools 模块里,如果想要使用它,则需要通过引入 functools 模块来调用 reduce() 函数:

from functools import reduce

filter()

filter() 函数用于过滤序列,过滤掉不符合条件的元素,Python2返回过滤后的列表,Python3返回迭代器对象(filter),如果要转换为列表,可以使用 list() 来转换。

# filter(function, iterable)
## 参数 —— function : 判断函数; iterable : 可迭代对象
## 返回值 —— 返回一个迭代器对象

# 过滤出列表中的所有奇数
def is_odd(n) :            
    return n % 2 == 1
 
>>> list(filter(is_odd, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]))
[1, 3, 5, 7, 9]

# 过滤出1~100中平方根是整数的数
import math
def is_sqr(n) :            
	return math.sqrt(n) % 1 == 0
 
>>> list(filter(is_sqr, range(1, 101)))
[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

**注意:**Python3 中返回到是一个 filter 类,filter 类实现了 __iter____next__ 方法,可以看成是一个迭代器, 有惰性运算的特性,相对 Python2提升了性能,可以节约内存。


sorted()

sorted() 函数对所有可迭代的对象进行排序操作。

sort 与 sorted 区别:

  • sort 是应用在 list 上的方法,sorted 可以对所有可迭代的对象进行排序操作。
  • list 的 sort 方法返回的是对已经存在的列表进行操作,而内建函数 sorted 方法返回的是一个新的 list,而不是在原来的基础上进行的操作。

sorted() 语法:

# sorted(iterable, key=None, reverse=False)  
## 参数 —— iterable -- 可迭代对象; key -- 主要是用来进行比较的元素,只有一个参数,具体的函数的参数就是取自于可迭代对象中,指定可迭代对象中的一个元素来进行排序; reverse -- 排序规则,reverse = True 降序 , reverse = False 升序(默认)
## 返回值 —— 重新排序的列表
>>>sorted([5, 2, 3, 1, 4])
[1, 2, 3, 4, 5]  

# 使用集合的sort()方法,实现类似结果
>>>a = [5,2,3,1,4]
>>> a.sort()
>>> a
[1,2,3,4,5]

# sort()及sorted()的区别在于, sort()返回None,排序操作直接作用在原list上,而sorted()排序后会返回新的list
>>> a = [1,3,2,4];id(a); a.sort(); id(a)
1734959842440
1734959842440
>>> b = [4,3,1,2]; id(b);id(sorted(b));
1734959953864
1734959953672

# sort()和sorted() 另一个区别在于list.sort() 方法只为 list 定义。而 sorted() 函数可以接收任何的 iterable。
>>> sorted({1: 'D', 2: 'B', 3: 'B', 4: 'E', 5: 'A'})
[1, 2, 3, 4, 5]

# 利用key进行倒序排序
>>> example_list = [5, 0, 6, 1, 2, 7, 3, 4]
>>> result_list = sorted(example_list, key=lambda x: x * -1)
>>> print(result_list)
[7, 6, 5, 4, 3, 2, 1, 0]

# 进行反向排序,也可传入第三个参数 reverse=True:
>>>example_list = [5, 0, 6, 1, 2, 7, 3, 4]
>>> sorted(example_list, reverse=True)
[7, 6, 5, 4, 3, 2, 1, 0]

返回函数

高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回。

# 返回累加函数变量
def lazy_sum(*args):	# 外部函数
     def sum():		#内部函数 可以引用外部函数lazy_sum的参数和局部变量
         ax = 0
         for n in args:
             ax = ax + n
         return ax
     return sum		# 返回函数变量(返回函数sum时,lazy_sum相关参数和变量都保存在返回的函数中,闭包)

>>> f = lazy_sum(1, 3, 5, 7, 9)
>>> f
<function lazy_sum.<locals>.sum at 0x00000193F3C89048>
>>> f()		# 返回的函数并没有立刻执行,而是直到调用了f()才执行
25

# 调用lazy_sum()时,每次调用都会返回一个新的函数
>>> f1 = lazy_sum(1, 3, 5, 7, 9)
>>> f2 = lazy_sum(1, 3, 5, 7, 9)
>>> f1==f2	# f1()和f2()的调用结果互不影响
False

闭包

一个函数返回了一个内部函数,该内部函数引用了外部函数的相关参数和变量,我们把该返回的内部函数称为闭包(Closure)

  • 闭包的最大特点就是引用了自由变量,即使生成闭包的环境已经释放,闭包仍然存在。
  • 闭包在运行时可以有多个实例,即使传入的参数相同。
# 调用lazy_sum()时,每次调用都会返回一个新的函数
>>> f1 = lazy_sum(1, 3, 5, 7, 9)
>>> f2 = lazy_sum(1, 3, 5, 7, 9)
>>> f1 == f2	# f1()和f2()的调用结果互不影响
False
  • 利用闭包,还可以模拟类的实例。
# 构造一个类,用于求一个点到另一个点的距离:
from math import sqrt

>>> class Point(object):
     def __init__(self, x, y):
         self.x, self.y = x, y

     def get_distance(self, u, v):
         distance = sqrt((self.x - u) ** 2 + (self.y - v) ** 2)
         return distance

>>> pt = Point(7, 2)        # 创建一个点
>>> pt.get_distance(10, 6)  # 求到另一个点的距离
5.0

# 用闭包来实现:
def point(x, y):
    def get_distance(u, v):
        return sqrt((x - u) ** 2 + (y - v) ** 2)

    return get_distance

>>> pt = point(7, 2)
>>> pt(10, 6)
5.0

注意: 尽量避免在闭包中引用循环变量,或者后续会发生变化的变量。

def count():
    funcs = []
    for i in [1, 2, 3]:
        def f():
            return i
        funcs.append(f)
    return funcs

>>> f1, f2, f3 = count()
>>> f1()
3
>>> f2()
3
>>> f3()
3
# 原因在于上面的函数 f 引用了变量 i,但函数 f 并非立刻执行,当 for 循环结束时,此时变量 i 的值是3,funcs 里面的函数引用的变量都是 3,最终结果也就全为 3。

# 可以再创建一个函数,并将循环变量的值传给该函数
def count():
    funcs = []
    for i in [1, 2, 3]:
        def g(param):
            f = lambda : param    # 这里创建了一个匿名函数
            return f
        funcs.append(g(i))        # 将循环变量的值传给 g
    return funcs

>>> f1, f2, f3 = count()
>>> f1()
1
>>> f2()
2
>>> f3()
3

你可能感兴趣的:(Python,Python,函数式编程,高阶函数,闭包)