Python学习笔记四:函数式编程

xiaoxiao2021-02-28  14

学习资源:廖雪峰Python教程

教程链接点击此处

重点记录与c,java有区别的知识点,红色部分重点注意。

四、函数式编程

函数式编程就是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量。由于Python允许使用变量,因此Python不是纯函数式编程语言

1.高阶函数(Higher-order function)

(1)什么是高阶函数?

①变量可以指向函数:

f = abs #abs为内置的绝对值函数,将这个函数名赋给一个变量 print(f(-10)) #输出10,说明变量f指向了绝对值函数abs

②函数名也是变量:

对于abs()这个函数,可以把函数名abs看作一个变量,它指向一个绝对值函数:

abs = 10 #将整数10赋给abs print(abs) #输出10 print(abs(-10)) #报错,此时abs不再指向绝对值函数,而是一个整数10

函数能够以函数作为参数(传入函数)

def add(x, y, f): return f(x) + f(y) x, y = -2, 3 f = abs result = add(x, y, f) #将abs()函数作为第三个参数传入add()函数 print(result) #输出5,说明abs()成功作为一个参数传入了函数add()

④把函数作为参数传入,这样的函数称为高阶函数,函数式编程就是指这种高度抽象的编程范式。

(2)map/reduce

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

def f(x): return x * x; r = map(f, list(range(1, 9))) #将第一个参数指向的函数对象,依次作用于第二个参数指向的序列中的每一个元素 print(list(r)) #输出[1, 4, 9, 16, 25, 36, 49, 64] #map返回的是一个Iterator,是惰性序列,list()函数将这个Iterator计算成一个list

#输出['1', '2', '3', '4', '5', '6', '7', '8'],即将1-9的整数转化为1-9的字符串 print(list(map(str, list(range(1, 9)))))

②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 result = reduce(add, list(range(1, 5))) #将1,2,3,4相加 print(result) #输出10

③map()配合reduce()实现str转化为int的函数:

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()函数将字符串s依次转化为单个数字1,2,3,4,5 #reduce再将fn()函数作为第一个参数,1,2,3,4,5作为第二个参数进行计算 result = str2int('12345') print(result) #输出12345整数

(3)filter

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

def is_odd(n): return n % 2 == 1 result = list(filter(is_odd, [1, 2, 3, 4, 5, 6])) #利用filter()函数筛选奇数 print(result) #输出[1, 3, 5]

②利用filter实现埃及筛法求素数

#这是一个生成无限序列的生成器,构造一个从3开始的奇数序列:3,5,7,... def _odd_iter(): n = 1 while True: n = n + 2 yield n def _not_divisible(n): return lambda x : x % n > 0 #一个不断返回下一个素数的生成器 #这个函数中的it从初始序列3,5,7,9,11,13...经过filter函数的筛选,不断获得新序列 #5,7,11,13,17,... 不能被3整除的序列 #7,11,13,17,19,... 不能被5整除的序列 #11,13,17,19,23,... 不能被7整除的序列 def primes(): yield 2 #2也是素数,先返回一个2 it = _odd_iter() #获得序列3,5,7,... while True: n = next(it) #返回当前序列的第一个数 yield n it = filter(_not_divisible(n), it) #构造新序列 for n in primes(): if n < 100: print(n) else: break

(4)sorted

sorted()是Python内置的排序函数:

L = [8, 5, -1, 9, -4] result1 = sorted(L) result2 = sorted(L, reverse = True) #reverse参数将排序结果反序 result3 = sorted(L, key = abs) #接收一个key实现自定义排序 #key先将指定的函数将作用于list的每一个元素上,再key函数返回的结果序列进行排序 #按元素的绝对值排序 print(result1) #输出排序后的结果[-4, -1, 5, 8, 9] print(result2) #输出排序后的结果[9, 8, 5, -1, -4] print(result3) #输出排序后的结果[-1, -4, 5, 8, 9]

2.返回函数

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

def lazy_sum(*args): def sum(): #内部函数sum()可以引用外部函数lazy_sum的参数和局部变量 ax = 0 for n in args: ax = ax + n return ax return sum f = lazy_sum(1, 2, 3, 4) #此时的f指向一个函数对象sum(1, 2, 3, 4) result = f() #再次调用函数f() print(result) #输出求和结果10 f1 = lazy_sum(1, 2, 3, 4) f2 = lazy_sum(1, 2, 3, 4) print(f1 == f2) #输出False,因为每次都会返回一个新的函数 ②

②闭包:如果在一个函数的内部定义了另一个函数,外部的称为外函数,内部的称为内函数。在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用。这样就构成了一个闭包。

返回闭包时牢记一点:返回的内函数中不要引用任何循环变量,或者后续会发生变化的变量

def count(): fs = [] for i in range(1, 4): def f(): return i * i fs.append(f) return fs f1, f2, f3 = count() print(f1(), f2(), f3()) #输出9 9 9,并非预想中的1 4 9

一定要引用循环变量时,应当再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变:

def count(): def f(j): def g(): return j * j return g fs = [] for i in range(1, 4): fs.append(f(i)) #f(i)理科被执行,因此i的当前值被传入f() return fs f1, f2, f3 = count() print(f1(), f2(), f3())

3.匿名函数

L = list(map(lambda x : x * x, list(range(1,9)))) print(L) #输出[1, 4, 9, 16, 25, 36, 49, 64]

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

限制:只能有一个表达式,不写return,返回值就是该表达式的结果

好处:不用担心函数名冲突,可以赋给一个变量,也可以作为返回值

4.装饰器(Decorator)

①装饰器本质上是一个高级函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象

#log()是一个装饰器,接受一个函数作为参数并返回一个函数 def log(func): def wrapper(*args, **kw): #wrapper()可以接受任意参数的调用 print('call %s():' % func.__name__) #函数对象有一个__name__属性,可以拿到函数的名字 return func(*args, **kw) return wrapper @log #用@将装饰器log()置于函数now()的定义处,相当于执行了语句 now = log(now) def now(): print('2018-2-8 15:46') now() #输出call now(): # 2018-2-8 15:46 #在运行now()之前,先运行装饰器log()

在wrapper内部,首先打印,然后接着调用原始函数

②带参数的装饰器:

#带参数的装饰器 def log(text): def decorate(func): def wrapper(*args, **kw): print('%s %s():' % (text, func.__name__)) return func(*args, **kw) return wrapper return decorate @log('excute') def now(): print('2018-2-8 15:57') now()

运行now()时,相当于执行log('excute')(now)。这个语句首先执行log('excute')获得指向decorate()函数对象的返回值。再执行decorate(now),最终返回的是wrapper()函数对象

③上述经过装饰器装饰后的函数的__name__属性已经变为‘wrapper’,所以,需要把原始函数的__name__等属性复制到wrapper()函数中。一个完整的装饰器写法如下:

import functools #log()是一个不带参数的装饰器,接受一个函数作为参数并返回一个函数 def log(func): @functools.wraps(func) def wrapper(*args, **kw): #wrapper()可以接受任意参数的调用 print('call %s():' % func.__name__) #函数对象有一个__name__属性,可以拿到函数的名字 return func(*args, **kw) return wrapper @log #用@将装饰器log()置于函数now()的定义处,相当于执行了语句 now = log(now) def now(): print('2018-2-8 15:46') now() #输出call now(): # 2018-2-8 15:46 #在运行now()之前,先运行装饰器log()

或者带参数的装饰器:

#带参数的装饰器 import functools def log(text): def decorate(func): @functools.wraps(func) def wrapper(*args, **kw): print('%s %s():' % (text, func.__name__)) return func(*args, **kw) return wrapper return decorate @log('excute') def now(): print('2018-2-8 15:57') now()

5.偏函数

偏函数的作用就是,把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,调用这个新函数会更简单

import functools def add(x, y): return x + y add2 = functools.partial(add, y = 2) #将add()函数的y参数设置默认值为2,构成函数add2() result = add2(3) #3 + 2 print(result) #输出5

当函数的参数个数太多,需要简化时,使用functools.partial可以创建一个新的函数,这个新函数可以固定住原函数的部分参数,从而在调用时更简单。

转载请注明原文地址: https://www.6miu.com/read-2350310.html

最新回复(0)