python学习笔记-函数式编程-20200310

Karima ·
更新时间:2024-09-21
· 538 次阅读

文章目录函数式编程高阶函数(Higher-order function)map/reducefilter用filter求素数exercisesorted-排序算法exercise返回函数函数作为返回值闭包exercise匿名函数erercise小结装饰器定义使用示例exercise小结偏函数小结 函数式编程 函数:面向过程的程序设计的基本单元 函数式编程----Functional Programming,是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用。而允许使用变量的程序设计语言,由于函数内部的变量状态不确定,同样的输入,可能得到不同的输出,因此,这种函数是有副作用的。

特点: 允许把函数本身作为参数传入另一个函数,还允许返回一个函数
Python不是纯函数式编程语言

高阶函数(Higher-order function) 变量可以指向函数,如f = abs,此时把函数abs赋值给了变量f
同时,可以通过该变量来调用这个函数,如: f = abs f(-10) 10 函数名也是变量
函数名其实就是指向函数的变量,故函数名可以看成变量,
如:abs = 10,此时就无法调用abs(-10)这个函数了 传入函数:既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。
一个最简单的高阶函数: def add(x,y,f) return f(x)+f(y) add(-5,6,abs) #调用

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

map/reduce

1.map()

map()函数接收两个参数,一个是函数,一个是Iterable。 map()将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回
如:
函数:f(x)=x^2
list:[1,2,3,4,5,6,7,8,9]
把函数作用在list上,用map()实现: def f(x): return x*x r = map(f, [1, 2, 3, 4, 5, 6, 7, 8, 9]) #调用 list(r)

一行程序调用:
list( map(f, Iterable))
2. reduce()

reduce把一个函数作用在一个序列[x1, x2, x3, …]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算 效果:
reduce(f,[x1,x2,x3,x4])=f(f(f(x1,x2),x3),x4) 例子: #序列求和 from functools import reduce def add(x , y): return x + y #转换整数 def fn(x , y) return x * 10 + y reduce(add,[1,3,5,7,9]) #调用

**注意:**调用reduce前要加一句from functools import reduce

补充:lambda用法:lambda 变量:表达式
练习1
利用map()函数,把用户输入的不规范的英文名字,变为首字母大写,其他小写的规范名字。输入:[‘adam’, ‘LISA’, ‘barT’],输出:[‘Adam’, ‘Lisa’, ‘Bart’]: def normalize(name): name=name[0].upper()+name[1:].lower() return name

练习2
Python提供的sum()函数可以接受一个list并求和,请编写一个prod()函数,可以接受一个list并利用reduce()求积:

def prod(L): def fn(x,y): return x*y return reduce(fn,L)

练习3
利用map和reduce编写一个str2float函数,把字符串’123.456’转换成浮点数123.456:

def str2num(s): D={'1':1,'2':2,'3':3,'4':4,'5':5,'6':6,'7':7,'8':8,'9':9,'0':0,'.':'.'} def digits(x): return D[x] I=list(map(digits,s)) d=I.index('.') I.remove('.') def fn(x,y): return x*10+y return reduce(fn,I[:d])+reduce(fn,I[d:len(I)])*10**(-1*(len(I)-d))

其中:** 表示幂次

filter

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

用filter()这个高阶函数,关键在于正确实现一个“筛选”函数。
e.g: def is_odd(n): return n % 2 == 1 #删除偶数 def not_empty(s): return s and s.strip() #删除序列中的空字符串 list(filter(is_odd, [1, 2, 4, 5, 6, 9, 10, 15])) list(filter(not_empty, ['A', '', 'B', None, 'C', ' '])) #调用 strip() :用于移除字符串头尾指定的字符(默认为空格或换行符)或字符序列 filter()函数返回的是一个Iterator,也就是一个惰性序列 用filter求素数 # 构造一个从3开始的奇数序列 def _odd_iter(): n = 1 while True: n = n + 2 yield n # 定义一个筛选函数 def _not_divisible(n): return lambda x: x % n > 0 #返回取余大于零的x,此时n为一个序列的首元素,x是序列中的全部元素 # 定义一个生成器,不断返回下一个素数 def primes(): yield 2 it = _odd_iter() # 初始序列 while True: n = next(it) # 返回序列的第一个数 yield n it = filter(_not_divisible(n), it) # 构造新序列,此时会去掉序列的第一个数 exercise

回数是指从左向右读和从右向左读都是一样的数,例如12321,909。请利用filter()筛选出回数:

个人解法 def is_palindrome(n): a=str(n) x=0 if n//10==0: return n else: while x <len(a)//2: if a[x]!=a[len(a)-1-x]: return False else: x=x+1 return n 参考大神解法 def is_palindrome(n): return int(str(n)[::-1])==n

其中:s[::-1] 为将字符串反转

sorted-排序算法 sorted():可以对list进行排序 sorted()可以接收一个key函数来实现自定义的排序,key指定的函数将作用于list的每一个元素上,并根据key函数返回的结果进行排序例如按绝对值大小排序: sorted([36, 5, -12, 9, -21], key=abs) 对字符串排序:按照ASCII的大小比较的 对字符串排序,按照字母序排序,秩序加key=str.lower sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower) ['about', 'bob', 'Credit', 'Zoo'] 反向排序,可以再次传入参数reverse=True sorted(['bob],'about,'Zoo','Credit],key=str.lower,reverse=True) exercise

假设我们用一组tuple表示学生名字和成绩:
L = [(‘Bob’, 75), (‘Adam’, 92), (‘Bart’, 66), (‘Lisa’, 88)]
请用sorted()对上述列表分别按名字排序,再按成绩从高到低排序:

# 方法一: def by_name(t): return t[0] def by_score(t): return t[1] L2 = sorted(L, key=by_name) L2 = sorted(L, key=by_score) #方法二: # 直接在sorted函数里面写lambda函数 L2=sorted(L,lambda t : t[0]) L2=sorted(L,lambda t : t[1]) 返回函数 函数作为返回值

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

def lazy_sum(*args): def sum(): ax=0 for n in args: ax=ax+n return ax return sum

当我们调用lazy_sum()时,返回的并不是求和结果,而是求和函数:

f = lazy_sum(1, 3, 5, 7, 9) # 此时f为求和函数 f() # 调用函数f时才真正计算求和的结果 当我们调用lazy_sum()时,每次调用都会返回一个新的函数,即使传入相同的参数 f1 = lazy_sum(1, 3, 5, 7, 9) f2 = lazy_sum(1, 3, 5, 7, 9) f1和f2的调用结果互不影响 闭包 上述例子中,我们在函数lazy_sum中又定义了函数sum,并且,内部函数sum可以引用外部函数lazy_sum的参数和局部变量,当lazy_sum返回函数sum时,相关参数和变量都保存在返回的函数中,这种称为“闭包(Closure)”的程序结构拥有极大的威力。 返回的函数并没有立刻执行,而是直到调用了f()才执行 返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量。 如果一定要引用循环变量,方法是再创建一个函数,用该函数的参数绑定循环变量当前的值,无论该循环变量后续如何更改,已绑定到函数参数的值不变: 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

缺点是代码较长,可利用lambda函数缩短代码。

exercise

利用闭包返回一个计数器函数,每次调用它返回递增整数:

# 方法一:生成器 def createCounter(): def num_generator(): num=0 while True: num+=1 yield num int_num=num_generator() def counter(): return next(int_num) return counter # 方法二:直接用变量count计数 def createCounter(): count=0 def counter(): nonlocal=counter count=count+1 return count return counter() # 方法三:使用list对象计数 def createCounter(): count=[0] def counter(): count[0]+=1 return count[0] return counter()

注:

使用生成器,按照生成器的做法在counter()函数中调用next()即可 使用闭包知识,且使用变量n(int对象),需要了解的是变量的作用域遵循LEGB规则,然后在counter()函数内声明nonlocal n 使用闭包知识,使用列表做计数器,无需在counter()函数内声明nonlocal n 匿名函数

当我们在传入函数时,有些时候,不需要显式地定义函数,直接传入匿名函数更方便。

匿名函数: lambda 变量列表:表达式
如以map()函数为例,计算f(x)=x^2:

list(map(lambda x : x * x,[1,2,3,4,5,6]])) 匿名函数只能由一个表达式,不用写return,返回值就是该表达式的结果 匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量来调用该函数: f=lambda x : x * x f(5) 也可以把匿名函数作为返回值返回: def builf(x , y) : return lambda : x * y erercise

请用匿名函数改造下面的代码:
def is_odd(n):
return n % 2 == 1

L = list(filter(is_odd, range(1, 20)))

L=list(filter(lambda x:x%2==1,range(1,20))) 小结

Python对匿名函数的支持有限,只有一些简单的情况下可以使用匿名函数。

装饰器 由于函数也是一个对象,而且函数对象可以被赋值给变量,所以,通过变量也能调用该函数 函数对象有一个__name__属性,可以拿到函数的名字
如:函数def now():
print(‘a’)
f=now()
now.__name__ —>‘now’ 定义

在代码运行期间动态增加功能的方式,称之为“装饰器”(Decorator)

本质上,decorator就是一个返回函数的高阶函数 使用示例 定义一个能打印日志的decorator # 函数 def log(func): def wrapper(*args, **kw): print('call %s():' % func.__name__) return func(*args, **kw) return wrapper # 调用 @log def now(): # -->now=log(now),这里的func就是now() print('2015-3-25') # 执行 now() 要自定义log的文本,此时decorator本身需要传入参数,新韩淑要返回decorator: # 函数 def log(text) : def decorator(func) : def wrapper(*args.**kw) : print('s% s%():' % (text,func.__name__)) return func(*args,**kw) return wrapper return decorator # 调用 @log('execute') def now(): print('2015-3-25') # 执行 now() -->now = log('execute')(now) # 结果 execute now(): 2015-3-25 经过decorator装饰之后的函数,__\name__发生改变,此时需要使用functools.wraps使得函数名字不变。
在定义装饰函数wrapper()之前加一句@functools.wrap(func) import functools def log(func): @functools.wrap(func) def wrapper(*args,**kw): print('call %s()' % func.__name__) return func(*args,**kw) return wrapper exercise

请设计一个decorator,它可作用于任何函数上,并打印该函数的执行时间:

import functools def log(text): if isinstance(text,str): def decorator(func): @functools.wraps(func) def wrapper(*args,**kw): print(text) print('begin call') #在函数调用的前后打印出'begin call'和'end call'的日志。 print('%s executed in %s ms' % (func.__name__, 10.24)) print('end call') return func(*args,**kw) return wrapper return decorator else: @functools.wraps(text) def wrapper(*args,**kw): print('begin call') print('%s executed in %s ms' % (text.__name__, 10.24)) print('end call') return text(*args,**kw) return wrapper @log def f1(x,y): print(x+y) @log('execute') def f2(x,y): print(x*y) f1(1,2) f2(1,2) 小结 在面向对象(OOP)的设计模式中,decorator被称为装饰模式 OOP的装饰模式需要通过继承和组合来实现,而Python除了能支持OOP的decorator外,直接从语法层次支持decorator Python的decorator可以用函数实现,也可以用类实现 偏函数 Python的functools模块提供了很多有用的功能,其中一个就是偏函数(Partial function) int(‘12345’, base=8) #将字符串转换为八进制,若不写base,默认为转换为十进制

创建偏函数的方法

functools.partial-----把一个函数的某些参数给固定住(也就是设置默认值),返回一个新的函数,如# b:,其中int为int()函数,base=2为固定其参数 创建偏函数时,实际可接收函数对象、*agrs和 ** kw这三个参数,如# c: # 函数,字符串转化为二进制整数 # a. def int2(x,base=2): return int(x,base) # b. import functools int2=functools.partial(int,base=2) #b比a更简单 # c. kw={'base':2} int('10010',**kw) ## 三种写法等价,都固定了int()函数的关键字参数base # 调用 int2('100000')

当然也可以更改默认值: int2('10000',base=10)

使用functools前要先输入import functools 小结

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


作者:yummydeli



python学习 函数 函数式编程 Python

需要 登录 后方可回复, 如果你还没有账号请 注册新账号