高阶函数
map
reduce
filter
sort
返回一个函数
闭包
匿名函数
装饰器
偏函数
1.特点
函数式编程允许把函数本身传入另一个函数,并且允许返回另一个函数。
2.高阶函数
高阶函数可以接收一个函数作为参数;
f = abs
a = 1
b = -2
# 将函数作为参数传入到另一个函数
def submit(x, y, z):
return z(x) + z(y)
print(submit(a, b, f))
输出结果:
3
3.map函数
map函数接收两个参数,一个是函数,另一个是可变参数形式的可迭代对象Iterable,有几个参数,map的第二个参数就传几个Iterable,map会将传的函数依次作用到Iterable的每个值,并把结果作为新的Iterator返回。
def f1(x):
return x * x
# r是一个Iterator
r = map(f1, [1, 2, 3])
print(list(r))
输出结果:
[1, 4, 9]
def f2(x, y):
return x * y
# r是一个Iterator
r = map(f2, [1, 2, 3], [1, 2, 4])
print(list(r))
输出结果:
[1, 4, 12]
4.reduce函数
reduce函数接收两个参数,一个是必须有两个参数的函数,另一个是一个可迭代对象Iterable,reduce会将传入的函数作用于Iterable的每一个值,将前两个元素的结果作为第一个参数继续与下一个参数作运算;
def f3(x, y):
return x + y
r1 = reduce(f3, [1, 2, 3, 4, 5])
print(r1)
输出结果:
15
计算方式为f(f(f(f(1,2),3),4),5)
5.将一个string类型的数字转为int类型
# str转Int
def str2int(s):
def f4(x, y):
return x * 10 + y
def f5(s1):
return {"0": 0, "1": 1, "2": 2, "3": 3, "4": 4, "5": 5, "6": 6, "7": 7, "8": 8, "9": 9}[s1]
return reduce(f4, map(f5, s))
print(str2int("123456"))
输出结果:
123456
6.filter函数
filter函数接收两个参数,一个是函数,另一个是一个序列,filter会将传入的函数作用于Iterable的每一个值,如果结果返回true,则保留,否则丢弃;
# 筛选出一个序列中所有能整除2的数
def f1(i):
return i % 2 == 0
l = filter(f1, [1, 2, 3, 4, 5])
print(list(l))
输出结果:
[2, 4]
7.Sort函数
# 普通大小排序
l = [2, 5, 1, -3, 6]
print(sorted(l))
# 转换成正数后再排序
print(sorted(l, key=abs))
# 英文排序,按首字母的ascll码的大小排序
l1 = ["Ckl", "Gak", "adfj", "bafk"]
print(sorted(l1))
# 转换成小写后再排序
print(sorted(l1, key=str.lower))
print(sorted(l1, key=str.lower, reverse=True))
输出结果:
[-3, 1, 2, 5, 6]
[1, 2, -3, 5, 6]
['Ckl', 'Gak', 'adfj', 'bafk']
['adfj', 'bafk', 'Ckl', 'Gak']
['Gak', 'Ckl', 'bafk', 'adfj']
8.返回一个函数
例如有一个求和的函数
def f1(index):
count = 0
for i in range(index):
count = count + i
return count
print(f1(5))
输出结果:
10
但是有时并不需要立即求和,这时就可以返回这个求和函数
def f2(index):
def f3():
count = 0
for i in range(index):
count = count + i
return count
return f3
f = f2(5)
print(f2(5))
print(f)
print(f())
输出结果:
<function f2.<locals>.f3 at 0x0000000001162510>
<function f2.<locals>.f3 at 0x0000000001162488>
10
注意:运行f()才会进行计算,11行的f和12行的f2(5)的不是同一个对象,所以结果也互相不影响,内部函数f3中引用了外部函数的变量index,当调用f2返回f3时,相关参数和变量会被保存在返回的函数中,这种形式被称为闭包。
9.闭包注意事项
def f1():
count = []
for i in range(3):
def f2():
return i * i
count.append(f2)
return count
m = f1()
print(m[0](), m[1](), m[2]())
例如上面的程序,常规会以为结果为0 1 4,但实际上结果为4 4 4,这是因为返回的函数并未立即执行,而是等到调用了f1()时才去执行,而这时的i已经变成了2了,所以三次的结果都为4,所以返回函数中不要引用后续会发生变化的变量;
如果一定要引用循环变量,可以再创建一个函数,用该函数的参数来绑定循环变量的值,这样就算该变量后续改变了,已经绑定到函数的参数还是不会变的;
def f3():
count = []
def f4(j):
def f5():
return j * j
return f5
for i in range(3):
# 带括号的方法会立即执行,所以用的是i的当前值
count.append(f4(i))
return count
m1 = f3()
print(m1[0](), m1[1](), m1[2]())
输出结果:
0 1 4
10.匿名函数
在python中,用关键字lambda表示匿名函数
常规函数:
def f1(x):
return x + x
print(list(map(f1, [1, 2, 3])))
输出结果:
[2, 4, 6]
使用匿名函数:
print(list(map(lambda x: x + x, [1, 2, 3])))
输出结果:
[2, 4, 6]
11.装饰器
例如已有一个函数f1,此时想在执行f1函数之前和之后做一些其它的事情,但是又不允许修改f1的代码,此时就可以用装饰器,装饰器可以增强函数的功能;
def d(func):
def wrapper(*args, **kwargs): # 一个可变参数加一个关键字参数可接收任意参数的调用
print('before call %s():' % func.__name__) # .__name__为获取该函数的name的方法
func1 = func(*args, **kwargs)
print('after call %s():' % func.__name__)
return func1
return wrapper
@d
def f1():
print("f1 print")
f1()
输出结果:
before call f1():
f1 print
after call f1():
通过在f1函数前@d的方式调用f1()时,相当于调用了d(f1),d()返回了一个函数wrapper(),wrpper函数里会运行作为参数传入d方法的f1()函数,并且在运行这个原始函数前后可以做一些需要增强的功能,例如这里在运行f1()函数前后输出一条日志;
如果想传一些参数到装饰器里,可以这样写:
def d1(msg):
def d(func):
def wrapper(*args, **kwargs):
print('before call %s():' % func.__name__, msg)
func1 = func(*args, **kwargs)
print('after call %s():' % func.__name__, msg)
return func1
return wrapper
return d
@d1('ssssss')
def f2():
print("f2 print")
f2()
输出结果:
before call f2(): ssssss
f2 print
after call f2(): ssssss
此时如果调用下面语句会发现一个问题:
print(f2.__name__)
输出结果:
wrapper
明明是f2,却变成了装饰器里的wrapper;
原因分析:
前面d的方式调用f1()时实际是这样的
f1 = d(f1)
然后d()函数又返回了一个wrapper函数;
后面d1的方式调用f2()时实际是这样的
f2 = d1("ssssss")(f2)
然后d1()函数返回了一个参数为f2的d()函数,d()函数又返回了wrapper函数,所以两种方式最终都返回了wrapper函数,经过装饰后的函数的name已经从原来的f1和f2变成了wrapper了。
想让原始函数的名字不变,就得把原始函数的name等属性复制到wrapper函数中,Python内置函数functools.wraps可以做这个事;
def d1(msg):
def d(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print('before call %s():' % func.__name__, msg)
func1 = func(*args, **kwargs)
print('after call %s():' % func.__name__, msg)
return func1
return wrapper
return d
这时再运行
print(f2.__name__)
就会得到f2了。
12.偏函数
在基础1里了解到了五种参数类型,其中有一种是默认参数,可以在方法定义参数时直接定义好默认参数,如果调用方法时不传值,会默认取定义的默认值;
def f1(x, y=2):
print(x, y)
f1(1)
f1(1, 3)
输出结果:
1 2
1 3
例如此时有一个需求是调用多次f1方法,而且要传一个与定义的默认值不同的值,这时如果写多次f1(1,y=6)肯定是不爽的,不如直接定义一个新的默认值为6的方法,这时再写一个和f1逻辑完全一样的方法也是不合适的,因为就默认值的定义变了下,此时可以用Python内置偏函数functools.partial()来直接创建一个新的函数f2:
f2 = functools.partial(f1, y=6)
f2(1)
f2(6)
输出结果:
1 6
6 6
再例如Python内置的int()函数,可以把字符串转换成整数型
# 默认十进制
print(int('123'))
# 按16进制输出
print(int('123', base=16))
输出结果:
123
291
此时如果要进行多次按16进制运算某些值,就得写多次base=16,而且int()方法是内置的也无法修改,这时用functools.partial()来修改这个默认值就再适合不过了
int16 = functools.partial(int, base=16)
print(int16('123'))
输出结果:
291