特点: 允许把函数本身作为参数传入另一个函数,还允许返回一个函数
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()
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))
其中:** 表示幂次
和map()类似,filter()也接收一个函数和一个序列。和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。
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', ' '])) #调用
# 构造一个从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) # 构造新序列,此时会去掉序列的第一个数
回数是指从左向右读和从右向左读都是一样的数,例如12321,909。请利用filter()筛选出回数:
def is_palindrome(n):
a=str(n)
x=0
if n//10==0:
return n
else:
while x
def is_palindrome(n):
return int(str(n)[::-1])==n
其中:s[::-1] 为将字符串反转
sorted([36, 5, -12, 9, -21], key=abs)
sorted(['bob', 'about', 'Zoo', 'Credit'], key=str.lower)
['about', 'bob', 'Credit', 'Zoo']
sorted(['bob],'about,'Zoo','Credit],key=str.lower,reverse=True)
假设我们用一组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时才真正计算求和的结果
f1 = lazy_sum(1, 3, 5, 7, 9)
f2 = lazy_sum(1, 3, 5, 7, 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
缺点是代码较长,可利用lambda函数缩短代码。
利用闭包返回一个计数器函数,每次调用它返回递增整数:
# 方法一:生成器
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]]))
f=lambda x : x * x
f(5)
def builf(x , y) :
return lambda : x * y
请用匿名函数改造下面的代码:
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对匿名函数的支持有限,只有一些简单的情况下可以使用匿名函数。
在代码运行期间动态增加功能的方式,称之为“装饰器”(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()
# 函数
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
import functools
def log(func):
@functools.wrap(func)
def wrapper(*args,**kw):
print('call %s()' % func.__name__)
return func(*args,**kw)
return wrapper
请设计一个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)
创建偏函数的方法
# 函数,字符串转化为二进制整数
# 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可以创建一个新的函数,这个新函数可以固定住原函数的部分参数,从而在调用时更简单。