高阶函数介绍
Higher-order function
- 变量可以指向函数
- 函数名又可以是变量
def hello():
print("hello world!")
f=hello
print(f)
abs=10
print(abs)
"""
<function hello at 0x0000023168ED9D08>
10
"""
由函数与变量的相互关系知道,可以把一个函数作为参数传入到另一个函数:
def hello(str):
print("hello "+str+"!")
def f(a,hello):
hello(a)
f("world",hello)
# hello world!
内置高阶函数
map
map()函数接收两个参数,一个是函数,一个是序列,map 将传入的函数依次作用到序列的每个元素 ,并把结果作为新的 list返回。
- 但实际上,在python3中,map()返回的是迭代器而不再是list,可使用list(map())的形式得到list
def times(x):
return x*x
a=map(times,[1,2,3,4,5])
print(a,list(a))
"""<map object at 0x0000023168CCF390> [1, 4, 9, 16, 25]"""
reduce
reduce()同样接受两个参数,一个是函数,一个是序列,reduce将累计运算结果与下一个元素计算,效果如下:
reduce(f,[a,b,c])=f(f(a,b),c)
reduce在python3当中已经被移除到了functlools模块中了,使用方法如下:
from functools import reduce
def add(a,b):
return a+b
c=reduce(add,[1,34,7,8])
print(c)
# 50
例子2:
from functools import reduce
def char2num(s):
return {'1':1,'2':2,'3':3,'4':4,'5':5,'6':6,'7':7,'8':8,'9':9,'0':0}[s]
def num2int(a,b):
return a*10+b
str1=reduce(num2int,map(char2num,"23523"))
print(str1)
# 23523
filter
filter函数同样接收两个参数,一个函数和一个序列,filter将函数应用于每个元素,然后再根据返回值是True或False来决定是否保留或丢弃元素。
例子1:
def is_odd(x):
return x%2==1
a=filter(is_odd,[233,4,5,65,7,7,8,6])
print(list(a))
# [233, 5, 65, 7, 7]
"""只保留了奇数"""
例子2:
def del_none(s):
return s and s.strip #解析看下面
b=filter(del_none,["hello "," ","","hel lo"])
print(list(b))
"""['hello ', ' ', 'hel lo']"""
- and布尔逻辑演算:
- 如果布尔上下文中的所有都为真,那么and返回最后一个值
- 如果布尔上下文中有一个为假,那么and返回第一个假值
- or布尔逻辑演算:
- 从左到右进行演算找到第一个真值就返回,其余的忽略
- 如果找不到真值,就返回最后一个假值
"a" and "b" and "c"
'c'
"a" and "" and "c"
''
"a" or "b"or "c"
'a'
"a" or ""or "c"
'a'
"" or "a"or "c"
'a'
0 or ""or []
[]
sorted
sorted用来对可迭代对象排序,通常规定,对于两个元素x和y,如果认为 x < y,则返回-1 ,如果认为 x == y,则返回0 ,如果认为 x > y,则返回1
a=sorted([12,5,2,47,9])
b=sorted([12,5,2,47,9],reverse=True)
print(a,b)
c=[(123,"A",34),(23,"D",34),(65,"B",84)]
d=sorted(c,key=lambda x:x[1])
print(d)
"""
[2, 5, 9, 12, 47] [47, 12, 9, 5, 2]
[(123, 'A', 34), (65, 'B', 84), (23, 'D', 34)]
"""
将函数作为返回值
例子1:
def lazy_sum(*args):
"""total=0不能放在这里,会出错,因为这里定义的变量不能在内部函数sum()里面被修改,除非使用nonlocal关键字"""
def sum():
total=0
for i in args:
total=total+i
return total
return sum
f=lazy_sum(23,5,63)
print(f)
f()
- 内部函数sum可以调用外部函数lazy_sum的参数或局部变量(只能使用不能修改,修改需加关键字nonlocal),相关的参数和变量都保存在了返回的函数中,这种程序结构称为 ”闭包“
- lazy_sum被f调用时,返回的并不是结果,而是sum函数
- 当调用f时才会返回最终结果
针对total=0位置的改变,代码可以修改成如下:
def lazy_sum(*args):
total=0
def sum():
nonlocal total
for i in args:
total=total+i
print(total)
return sum
f=lazy_sum(23,5,63)
print(f)
f()
闭包函数:
概述: 在外部函数中定义了内部函数,内部函数使用了外部函数的参数或者变量,外部函数的返回是内部函数的引用。
例子1:
def outter(a):
b=2
def inner():
print(a+b)
return inner
c=outter(4)
c()
# 6
例子2:
def outter():
fs=[]
for i in range(4):
def f():
return i*i
fs.append(f)
return fs
a=outter()
print(a)
"""[<function outter.<locals>.f at 0x000001555C584400>, <function outter.<locals>.f at 0x000001555C584488>, <function outter.<locals>.f at 0x000001555C584510>, <function outter.<locals>.f at 0x000001555C584598>]"""
a[1](),a[2](),a[3]()
#(9, 9, 9)
- 分析:
- 从结果可知,三个值都是9,而不是[1,4,9],这是因为循环创建的3个函数f都没有被立刻执行,导致每个函数里面的 i值最终都变成了3,所以调用时候结果都为9
- 因此,返回函数里建议不要使用循环变量,如果要使用,则再建一个函数,修改如下:
def outter():
fs=[]
for i in range(4):
def f(j):
def g():
return j*j
return g
fs.append(f(i))
return fs
a=outter()
a[1](),a[2](),a[3]()
# (1, 4, 9)
匿名函数
没有名字的函数,同时匿名函数也是一个函数对象,可以把匿名函数赋值给一个变量
a=map(lambda x:x*x,[2,3,5,6,7])
print(list(a))
装饰器
在代码运行期间 动态增加函数功能 的方式称之为“装饰器”
例子1:
def now():
print("2019/7/5")
now()
"""2019/7/5"""
"""下面给now()函数增加功能"""
def log(func):
def wrapper(*args,**kw):
print("call {}".format(func.__name__))
return func(*args,**kw)
return wrapper
@log
def now():
print("2019/7/5")
""""重新运行now()函数:"""
now()
"""
call now
2019/7/5"""
print(now)
<function log.<locals>.wrapper at 0x000001C87FC78400>
now.__name__
"""wraper"""
分析:
- 由print(now)输出可以知道,now变量已经指向了另一个函数wrapper
- 执行装饰后的now()函数,相当于执行log(now())
- 学习过程的疑惑:为什么a=log(now())直接返回结果了,而不是返回对象,分析如下:
- a=log(now())可以拆分成两步:
- a=log #此步返回wrapper函数对象
- a(now()) # 执行结果运算输出
- a=log(now())可以拆分成两步:
- 每个函数都有一个属性 .__name__,返回函数的名字
例子2: 在上面的例子基础上 给装饰器添加参数,代码修改如下:
def decorator(parameter):
def log(func):
def wrapper(*args,**kw):
print("call {}".format(func.__name__))
print(parameter)
return func(*args,**kw)
return wrapper
return log
@decorator("hello world!")
def now():
print("2019/7/5")
now()
"""
call now
hello world!
2019/7/5
"""
print(now)
<function decorator.<locals>.log.<locals>.wrapper at 0x000001C87FE210D0>
now.__name__
"""wraper"""
分析:
- 由print(now)输出可以知道,now变量已经指向了另一个函数log
- now()相当于执行decorator(“hello world”)(now())
上面还不是装饰器的完整写法,下面继续补充:
- 以第一例子为例,now.__name__的结果为"wrapper",因为now变量名指向了wrapper函数,而wrapper函数的名字是“wrapper”
- 如果要把原始函数now的__name__等属性复制到wrapper函数中,从而使得now.__name__的结果还是"now",可以利用python内置的functools.wraps,这就是装饰器的完整写法,如下:
import functools
def log(func):
@functools.wraps(func) """不需要编写wrapper.__name__ = now.__name__这样的代码"""
def wrapper(*args,**kw):
print("call {}".format(func.__name__))
return func(*args,**kw)
return wrapper
@log
def now():
print("2019/7/5")
print(now)
now.__name__
# 'now'
偏函数
通过设定参数的默认值,可以 降低函数的调用难度,这个就是偏函数做的东西,是利用 functools.partial 实现的
例子1:
"""int可以把字符串转换为整数,并通过base=转为一定进制"""
int("2363")
2363
int("2363",base=8)
1267
int("2363",base=16)
9059
int2=functools.partial(int,base=16)
int2("2363")
905