特点: 允许把函数本身作为参数传入另一个函数,还允许返回一个函数
Python不是纯函数式编程语言
f = abs
,此时把函数abs赋值给了变量ff = abs
f(-10)
10
函数名也是变量abs = 10
,此时就无法调用abs(-10)
这个函数了
传入函数:既然变量可以指向函数,函数的参数能接收变量,那么一个函数就可以接收另一个函数作为参数,这种函数就称之为高阶函数。def add(x,y,f)
return f(x)+f(y)
add(-5,6,abs) #调用
小结
把函数作为参数传入,这样的函数称为高阶函数,函数式编程就是指这种高度抽象的编程范式。
1.map()
map()函数接收两个参数,一个是函数,一个是Iterable。 map()将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回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()
#序列求和
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
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()这个高阶函数,关键在于正确实现一个“筛选”函数。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__属性,可以拿到函数的名字在代码运行期间动态增加功能的方式,称之为“装饰器”(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使得函数名字不变。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.partial可以创建一个新的函数,这个新函数可以固定住原函数的部分参数,从而在调用时更简单。