python3 第二十章 - 函数式编程之Higher-order function(高阶函数)
什么是高阶函数?把函数作为参数传入或把函数做为结果值返回,这样的函数称为高阶函数,函数式编程就是指这种高度抽象的编程范式。函数式编程的特点:
- 函数本身可以赋值给变量,赋值后变量为函数;
- 允许将函数本身作为参数传入另一个函数;
- 允许返回一个函数。
在前面的章节中,我们知道可以用abs()这个函数来得到一个数的绝对值,如:
print('abs(-100):', abs(-100))
以上代码,输出:
abs(-100): 100
如果,我们把代码修改下,把abs赋值给一个变量:
f = abs
print(f(-100))
以上代码,输出:
100
可见,abs(-100)是函数调用,而abs是函数本身,要获得函数调用结果,我们可以把结果赋值给变量。函数本身也可以赋值给变量,即:变量可以指向函数,这时变量就获得了函数的功能,如上例中的 f ,直接调用f()和直接调用abs()完全相同。
那么函数名是什么呢?函数名其实就是指向函数的变量!对于abs()这个函数,完全可以把函数名abs看成变量,它指向一个可以计算绝对值的函数!
如果把abs指向其他对象,会有什么情况发生?
abs = 10
print(abs(-10))
把abs指向10后,就无法通过abs(-10)调用该函数了!因为abs这个变量已经不指向求绝对值函数而是指向一个整数10!当然实际代码绝对不能这么写,这里是为了说明函数名也是变量。
注:由于abs函数实际上是定义在import builtins模块中的,所以要让修改abs变量的指向在其它模块也生效,要用import builtins; builtins.abs = 10。关于什么是模块,后面会讲到,这里不要去纠结。
既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。
一个最简单的高阶函数:
def add(x, y, f):
return f(x) + f(y)
当我们调用add(-5, 6, abs)时,参数x,y和f分别接收-5,6和abs,根据函数定义,我们可以推导计算过程为:
x = -5
y = 6
f = abs
f(x) + f(y) ==> abs(-5) + abs(6) ==> 11
return 11
用代码验证下:
def add(x, y, f):
return f(x) + f(y) print(add(-5, 6, abs))
1、map函数
这是Python内置的一个函数,map()函数接收两个参数,一个是函数,一个是Iterable(迭代器对象),map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator(迭代器)返回:
def f(x):
"""
返回一个数的3次方
:param x: 数
:return: 3次方后的结果
"""
return x**3 r = map(f, [1, 2, 3, 4, 5]) print(list(r))
以上代码,输出:
[1, 8, 27, 64, 125]
map()传入的第一个参数是f,即函数对象本身。由于结果r是一个Iterator,Iterator是惰性序列,因此通过list()函数让它把整个序列都计算出来并返回一个list。
当然,不需要map()函数,写一个循环,也可以计算出结果:
def f(x):
"""
返回一个数的3次方
:param x: 数
:return: 3次方后的结果
"""
return x**3 L = []
for n in [1, 2, 3, 4, 5]:
L.append(f(n)) print(L)
执行后,结果是一样的,但这样的写法,我们并不能直观的知道f()是作用在列表的所有元素上,并返回一个列表,我们必须读了源代码后才知道。
所以,map()作为高阶函数,事实上它把运算规则抽象了,因此,我们不但可以计算简单的f(x)=x3,还可以计算任意复杂的函数,比如,把这个list所有数字转为字符串:
print(list(map(str, [1, 2, 3, 4, 5])))
以上代码,输出:
['', '', '', '', '']
当然,更多的时候我们也会配合lambda来使用,如:
r = map(lambda x: x**3, [1, 2, 3, 4, 5]) print(list(r))
以上代码,输出:
[1, 8, 27, 64, 125]
2、reduce函数
reduce把一个函数作用在一个序列[x1, x2, x3, ...]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算,其效果就是:
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)
对一个序列求和,就可以用reduce实现:
from functools import reduce def add(x, y):
return x + y print('reduce(add, [1, 3, 5, 7, 9]) = ', reduce(add, [1, 3, 5, 7, 9]))
以上代码,输出:
reduce(add, [1, 3, 5, 7, 9]) = 25
当然求和运算可以直接用Python内建函数sum(),思考:如何把序列 [1, 2, 3, 4, 5] 转化为整型的 12345 ,下面给出代码但请尽可能自己先写出
from functools import reduce def f(x, y):
return x*10 + y res = reduce(f, [1, 2, 3, 4, 5]) print(res)
思考:假如python没有提供 int() 函数,如何使用 map与reduce自己写一个函数,实现将 '123456' 转化为 ,下面给出代码但请尽可能自己先写出:
from functools import reduce def str2int(s):
"""
将字符串转为数字
:param s: 要转化的字符串
:return: 数字
""" DIGITS = {'': 0, '': 1, '': 2, '': 3, '': 4, '': 5, '': 6, '': 7, '': 8, '': 9}
def char2num(c):
"""
字符转数字
:param c:字符
:return: 数字
"""
return DIGITS[c] def f(x, y):
return x*10 + y resList = map(char2num, s) # 将字符串转为列表 return reduce(f, resList) # 将列表元素依次排列组成数字,并返回 print(str2int(''))
3、filter函数
Python内建的filter()函数用于过滤序列。filter()接收一个函数和一个序列,把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。
例如,在一个list中,删掉偶数,只保留奇数,可以这么写:
def is_odd(n):
return n % 2 == 1 res = list(filter(is_odd, [1, 2, 3, 4, 5, 6, 7, 8]))
print(res)
以上代码,输出:
[1, 3, 5, 7]
注:filter()函数返回的是一个Iterator,也就是一个惰性序列,filter()的作用是从一个序列中筛出符合条件的元素。由于filter()使用了惰性计算,所以只有在取filter()结果的时候,才会真正筛选并每次返回下一个筛出的元素。
4、sorted函数
排序也是在程序中经常用到的算法。无论使用冒泡排序还是快速排序,排序的核心是比较两个元素的大小。如果是数字,我们可以直接比较,但如果是字符串或者两个dict呢?直接比较数学上的大小是没有意义的,因此,比较的过程必须通过函数抽象出来。
Python内置的sorted()函数就可以对list进行排序:
print(sorted([36, 5, -12, 9, -21]))
以上代码,输出:
[-21, -12, 5, 9, 36]
它还可以接收一个key函数来实现自定义的排序,例如按绝对值大小排序:
print(sorted([36, 5, -12, 9, -21], key=abs))
以上代码,输出:
[5, 9, -12, -21, 36]
key指定的函数将作用于list的每一个元素上,并根据key函数返回的结果进行排序。
这个是数字,那字符串排序是怎么样的呢?我们来看实例:
print(sorted(['hello', 'world', 'roy', 'python', 'c++']))
以上代码,输出:
['c++', 'hello', 'python', 'roy', 'world']
默认情况下,对字符串排序,是按照ASCII的大小比较的,每个字符依次比较(两个字符串比较,先比较第一个字符,当第一个字符分出大小时则结束比较且比较的结果即为第一个字符相比较的结果,当第一个字符相同时则比较第二个字符,第二个字符相同时则比较第三个,依次下去直到得出结果),由于'Z' < 'a',结果,大写字母Z会排在小写字母a的前面。
可以用 reverse=True 进行反向排序:
print(sorted([36, 5, -12, 9, -21], reverse=True))
print(sorted(['hello', 'world', 'roy', 'python', 'c++'], reverse=True))
以上代码,输出:
[36, 9, 5, -12, -21]
['world', 'roy', 'python', 'hello', 'c++']
python3 第二十章 - 函数式编程之Higher-order function(高阶函数)的更多相关文章
- python3 第二十二章 - 函数式编程之Decorator(装饰器)
前面我们说了,在python中,一切皆对象.函数也是一个对象,而且函数对象可以被赋值给变量,通过变量也能调用该函数.如: def sayHello(name): print(name + ' hell ...
- Python函数式编程之map()
Python函数式编程之map() Python中map().filter().reduce()这三个都是应用于序列的内置函数. 格式: map(func, seq1[, seq2,…]) 第一个参数 ...
- 函数式编程之 Python
上接 python 函数式编程学习笔记 参考:www.sigai.cn/ 1 函数式编程概述 前提:函数在 Python 中是⼀等对象 工具:built-in ⾼阶函数:lambda 函数:opera ...
- Ramda函数式编程之PHP
0x00 何为函数式编程 网上已经有好多详细的接受了,我认为比较重要的有: 函数是"第一等公民",即函数和其它数据类型一样处于平等地位 使用"表达式"(指一个单 ...
- java函数式编程之lambda表达式
作为比较老牌的面向对象的编程语言java,在对函数式编程的支持上一直不温不火. 认为面向对象式编程就应该纯粹的面向对象,于是经常看到这样的写法:如果你想写一个方法,那么就必须把它放到一个类里面,然后n ...
- Python3基础(3)集合、文件操作、字符转编码、函数、全局/局部变量、递归、函数式编程、高阶函数
---------------个人学习笔记--------------- ----------------本文作者吴疆-------------- ------点击此处链接至博客园原文------ 1 ...
- [Python3] 035 函数式编程 高阶函数
目录 函数式编程 之 高阶函数 1. 引子 2. 系统提供的高阶函数 3. functools 包提供的 reduce 4. 排序 函数式编程 之 高阶函数 把函数作为参数使用的函数,叫高阶函数 1. ...
- Python3学习之路~3.2 递归、函数式编程、高阶函数、匿名函数、嵌套函数
1 递归 在函数内部,可以调用其他函数.如果一个函数在内部调用自身本身,这个函数就是递归函数. def calc(n): print(n) if int(n / 2) == 0: return n r ...
- 高阶函数 - Higher Order Function
一个函数如果有 参数是函数 或 返回值是函数,就称为高阶函数. 这篇文章介绍高阶函数的一个子集:输入 fn,输出 fn'. 按 fn 与 fn' 功能是否一致,即相同输入是否始终对应相同输出,把这类高 ...
随机推荐
- windows环境VS2015编译TensorFlow C++程序完全攻略
本文参考和综合了多篇网络博客文章,加以自己的实践,最终终于在windows环境下,编译出可以用于C++程序调用tensorflow API的程序,并执行成功. 考虑到网络上关于这方面的资料还较少,特总 ...
- Python 的编码格式
[前言] Python的编码格式对于初学者来说是很头疼的一件事,不过如果接触的多了,就会发现,只要在恰当的时候使用了恰好的编码,就不会出现太多的问题. [编码介绍] python 的编码格式2.x 和 ...
- wc--Linux
这个命令的功能也很好记,因为它功能很有限: wc -c filename:显示一个文件的字节数 wc -m filename:显示一个文件的字符数 wc -l filename:显示一个文件的行数 w ...
- 【转】Install libimobiledevice on Mac OSX
About the App App name: libimobiledevice App description: Library to communicate with iOS devices na ...
- 如何删除chrome地址栏里面曾经输错的地址
在chrome浏览器的地址栏输入你想删除的网址的部分字幕,比如,在地址栏输入form,然后用键盘上的方向键定位到你想删除的那个错误的地址,如下图所示 然后在键盘上按 shift+del 组合键将其 ...
- 12、ABPZero系列教程之拼多多卖家工具 拼团提醒功能登录拼多多实现
上篇文章已经完成了整个拼多多拼团提醒功能,本篇继续完成拼多多帐号登录,拼多多帐号登录的目的是为了获取拼团商品的SKU和订单号,便于商家备货. 以下是拼多多官方的后台登录,要实现的功能并不是直接在这里登 ...
- hdu_4497GCD and LCM(合数分解)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4497 GCD and LCM Time Limit: 2000/1000 MS (Java/Other ...
- Redis进阶实践之四Redis的基本数据类型
一.引言 今天正式开始了Redis的学习,如果要想学好Redis,必须先学好Redis的数据类型.Redis为什么会比以前的Memchaed等内存缓存软件使用的更频繁,适用范围更广呢?就是因为R ...
- 6.移植uboot-支持yaffs烧写,打补丁
在上一章,裁剪uboot以及分区后,本章主要使uboot支持yaffs以及制作补丁 1. 修改uboot支持yaffs 首先,每个命令都会对应一个文件,比如nand命令对应的common/cmd_na ...
- 科普:String hashCode 方法为什么选择数字31作为乘子
1. 背景 某天,我在写代码的时候,无意中点开了 String hashCode 方法.然后大致看了一下 hashCode 的实现,发现并不是很复杂.但是我从源码中发现了一个奇怪的数字,也就是本文的主 ...