1.高阶函数
定义函数时,将函数作为参数,就可叫做高阶函数,使用高阶函数编程就是函数式编程。
1.1变量可以指向函数,函数名也是变量
在python中,函数名也是变量,在之前的笔记中,可以给函数更换名字,例如。
代码:
my = abs # 函数名abs指向abs()函数,变量my也指向abs()函数
print(abs(-4))
print(my(-4))
结果:
4
4
如果函数名(即变量)abs指向了其他的数据,或者函数名abs的指向被删除了,则原来的abs()
函数不可再用函数名abs来调用,例如
代码:
abs = 1 # 变量abs已经指向整数1,不再指向abs()函数
# del abs 此句是删除abs的指向
print(abs(-4)) # 变量abs已经不再指向abs()函数了
结果:
TypeError: 'int' object is not callable
# NameError: name 'abs' is not defined
所以在定义函数时,变量可以作为参数,同样,函数名也可作为参数,将函数传递到另一个函数中。例如
代码:
# 定义平方的函数
def square(number):
return number ** 2
# 按照传递进去的函数逻辑,计算两个数的结果和
def test(x, y, method):
print(method(x) + method(y))
test(1, 3, square) # 传进去求平方的逻辑
test(1, -3, abs) # 传进去求绝对值的逻辑,注意是abs()函数的函数名,即变量abs
结果:
10
4
1.2map()
和redued()
函数的使用
map()
函数接收两个参数,第一个是函数,第二个是一个Iterable
(可迭代的对象),每个元素都将运行该函数,并将结果作为一个Iterator
返回(注意:Iterator
不能直接打印输出存储结果)
例如,求列表里每个数的3次方(其实循环也可以实现,但是用map()
函数更直观方便,更容易理解)
代码:
def cube(x): # 定义计算3次幂的函数
return x ** 3
nums = [1, 2, 3, 4, 5]
result = list(map(cube, nums)) # 使用map函数,将计算函数和数据传入,再通过list将对象变成列表对象
print(result)
结果:
[1, 8, 27, 64, 125]
reduce()
函数也接收两个参数,第一个是函数并且该函数必须有2两个参数,第二个参数是传进去的数据。reduce()
会按照传进的函数从数据中取2个元素,并将结果和下一个元素再次当做运算的2个元素,以此运算。使用reduce()
函数,需要导入,例如from functools import reduce
代码:
from functools import reduce
# 此函数没啥意义,纯粹是为了练习python语法而已→_→
def prod(L):
def mul(x, y): # 函数里定义了相乘的函数
return x * y
return reduce(mul, L) # 从L中取前两个运算,然后将结果和第三个再作为两个参数进行运算,以此类推
print('1*2*3*4 =', prod([1, 2, 3, 4]))
结果:
1*2*3*4 = 24
map()
和reduce()
结合的小栗子,同上,没啥意义,纯粹为了练习(⊙o⊙)…
将字符串转换成浮点数 strnum = '123.456'
,其实完全可以用float(strnum)
代码:
from functools import reduce # 导入reduce
strnum = '9876.1234'
def str2float(str=''):
nums = str.split('.') # split函数将按照传入的参数分隔字符串并封装到列表中返回
str1 = nums[0] # 小数点前的字符串
str2 = nums[1] # 小数点后的字符串
def char2num(s): # 构造字符转换成数字函数
return {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9}[s]
def str2num(x, y): # 转换成数字后再运算得出整数
return x * 10 + y
a = reduce(str2num, map(char2num, str1))
b = reduce(str2num, map(char2num, str2))
result = a + b / 10 ** (len(str2)) # 注意是10的n次幂运算
return result
print(str2float(strnum))
结果:
9876.1234
1.3filter()
函数
filter()
函数大致同map()
函数,也是接收一个函数和一个列表,不同的是,filter()
会将每次运行完传入的函数方法后判断,根据True
和False
来决定是否要保存这个元素,最后返回的也是一个Iterator
对象,需要用list()
才能显示出来。例如
代码:
def saveS(s): # 传入的字符创中如果有s返回True,否则返回False
return 's' in s
L = ['abc', 'dsef', 'asdf', 'ee', 'sht']
print(list(filter(saveS, L))) # 返回True时保留
结果:
['dsef', 'asdf', 'sht']
e.g. 用filter()
求素数,使用方法逻辑是埃氏筛法
代码:
# 初始化一个序列,从3开始的奇数(偶数不是素数)
def initNumber():
n = 1
while True:
n += 2
yield n
# 定义一个筛选函数,能被n整除的返回False,不能整除的返回True
def check(n):
return lambda x: x % n > 0
def getPrime(maxNum):
yield 2 # 生成器返回第一个素数
init = initNumber() # 调用初始化函数 3,5,7,9,11,13,15,17,19,21...
while True:
n = next(init) # 取得初始化的第一个数3
if n < maxNum:
yield n # 生成器返回第一个数3(第一个数总是素数)
init = filter(check(n), init) # 过滤3的倍数5,7,11,13,17,19... 如此循环
else:
break
print(list(getPrime(100)))
结果:
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47]
1.4sorted()
函数
在笔记(2)中,已经记录了对列表的排序,里面的sorted()
可以只传入列表进行临时排序,也可再添加按照相应的逻辑来排序(即传入函数名,使用命名关键字key=xxx
) 例如
代码:
L = ['a', 'c', 'M', 'F', 'e']
print(sorted(L)) # 原列表排序
print(sorted(L, reverse=True)) # 列表反序
print(sorted(L, key=str.lower)) # 按照元素的字母的ASCII码(无视大小写,函数名也可换做str.upper)
结果:
['F', 'M', 'a', 'c', 'e']
['e', 'c', 'a', 'M', 'F']
['a', 'c', 'e', 'F', 'M']
2.返回函数和闭包
函数不仅可以作为参数传递,还可作为返回值来返回,例如
代码:
def getInfo(name): # 函数A
def getAge(age): # 函数A中定义了函数B
return 'name=' + name + ',age=' + str(age)
return getAge # 返回函数,即指向函数B的变量(函数名)
test1 = getInfo('zhangsan') # 调用getInfo函数,返回一个函数,即之前定义好的getAge()函数,用test1指向它
test2 = getInfo('lisi')
print (test1(22)) # 再调用返回的函数
print (test2(22))
结果:
name=zhangsan,age=22
name=lisi,age=22
什么是闭包呢?简单的讲就是调用函数A,而A返回了一个函数B,并且B引用了传递给A的参数(或者是A内部定义的局部变量),这个函数B就叫做闭包,这个参数就是闭包的自由变量
如上述的代码中,调用getInfo()
时传递的参数(即自由变量)是不同的,即使闭包getAge()
调用的时候test1(22)
传入相同的参数,结果仍是不同的。
需要注意的是:返回的闭包函数没有立即执行,而是在调用(例如test1(22)
)的时候才会执行,举个栗子
代码:
def count(): # 函数A
fs = [] # 创建一个空列表
for i in range(1, 4): # 循环3次,i是A的变量
def f(): # 函数A中定义的函数B,引用了A的变量i
return i * i # 返回表达式(也是个函数),并没有直接调用
fs.append(f) # 添加到列表中,循环完毕后i的值已经是3
return fs
f1, f2, f3 = count() # f1,f2,f3分别指向列表中的3个函数
print (f1()) # 分别调用函数,注意,此时调用的时候直接从第五行代码开始执行,此时的i已经是3
print (f2())
print (f3())
结果:(结果不是1,4,9,可在debug模式下看执行步骤)
9
9
9
由此可见,在闭包函中的自由变量,或者是调用的A的局部变量尽量不要使用动态变化的。
3.匿名函数
先看代码,求一个数的3次方,可用如下代码
代码:
L = [1, 2, 3, 4, 5]
def cube(x):
return x ** 3
print (list(map(cube, L)))
结果:
[1, 8, 27, 64, 125]
如果某些函数只调用一次,可以用lambda
表达式来构造匿名函数,不用再显示的定义函数,如下
代码:
L = [1, 2, 3, 4, 5]
print (list(map(lambda x: x ** 3, L)))
结果:
[1, 8, 27, 64, 125]
lambda x: x ** 3
实际上就是上边定义的函数cube,第一个x是参数(可不写,即为无参函数),后边的是函数逻辑,整个表达式返回一个函数变量,匿名函数也可作为返回值。
4.装饰器
在定义好的函数运行期间,不修改此函数,动态地给该函数增添功能,即为装饰器Decorator
,说白了就是传递函数和返回函数的运用,下边是个最简单的例子,例如。详细请看
代码:
def deco(fun):
print('---运行之前---')
fun()
print('---运行之后---')
def myfun():
print('运行了函数')
deco(myfun) # 将函数作为参数传递
结果:
---运行之前---
运行了函数
---运行之后---
5.偏函数
如果调用某个函数的时候,该函数的参数过多,需要简化,可以使用functools
模块的partial
,例如
int()
可以将字符串转换成整数,只传入字符串时,默认按照十进制转化,若添加base
参数,则按照所传的进制计算
代码:
print(int('1234567'))
print(int('1234567', base=8))
print(int('1234567', base=16))
结果:
1234567
342391
19088743
除了每次都传入base参数,还可用partial
,例如
代码:(其实就是外部更改了函数参数默认值)
import functools
int2 = functools.partial(int, base=2) # int函数外固定其参数base默认值是2
print(int2('101010001001')) # 每次调用无需再传入参数base
print(int2('101101'))
print(int2('101101', base=8)) # 当手动传入base参数的时候,按照传入的数据计算
结果:
2697
45
33345