一、高阶函数
把函数作为参数传入,这样的函数成为高阶函数,函数式编程就是指这样高度抽象的编程范式 。
高阶函数:
实参:可以是一个函数的函数名;
返回值:可以是一个函数的函数名。
(一)内置高阶函数map()
map()函数会根据提供的函数对指定的序列做映射。其语法如下:
map(function, iterable, ...) #function --函数 iterable --一个或多个序列
第一个参数function以参数序列中的每一个元素调用function函数,返回包含每次function函数的返回
值的新列表(python2)或迭代器(python3)
def fun(x): return x**2 print(list(map(fun,range(1,10)))) #将一个存储0~9的列表的每一个元素平方
输出:
[1, 4, 9, 16, 25, 36, 49, 64, 81]当function函数拥有两个参数时,map()函数的可迭代的对象也就需要两个参数
def fun_1(x,y): return x+y print(list(map(fun_1,range(1,11),range(1,11)))) #计算两个列表每个元素相加的和
输出:
[2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
(二)内置高阶函数reduce()
Python2中reduce()函数在全局名字空间中,可以直接使用,在Python3中已经从全局名字空间中移除
了,被放置在function模块里面,如果想要使用它,则需要通过引入functools模块来调用reduce()函数。
from functools import reduce
reduce()函数的语法如下:
reduce(function,iterable[,initializer]) function--函数, iterable --可迭代对象 , initializer --可选,初始参数.
redeuce()函数会对参数序列中的元素进行累积,函数讲一个数据集合(链表,元组)中的所有有数据进行
下列操作:用传给reduce中的函数function(必须有两个参数)先对集合中的1,2个元素进行操作,在将得到的
结果与第三个数据用function函数运算,最后得到一个结果。
from functools import reduce def add(x,y): return x+y print(reduce(add,range(1,11))) #((((1+2)+3)+4)+5)+6)+... 计算1~10的元素之和.输出:
55
(三)内置高阶函数filter()
filter()函数用于过滤序列,过滤掉不符合条件的元素,返回由符合条件元素组成的新列表,该函数接收
两个参数,第一个为函数,第二个为序列序列的每个元素作为参数传递给函数进行判断,然后返回True或
Fase,最后将返回True的元素放到新的列表中。
filter(function,iterable) #function --判断函数 , iterable -- 可迭代的对象例子:
def chose_odd(num): return (num % 2 == 0) print(list(filter(chose_odd,range(1,11))))
输出:
[2, 4, 6, 8, 10]
注意:
关于filter()方法,在python2和python3中有所不同,python2中返回的是过滤后的列表, 而python3
中返回到是一个filter类,filter类实现了__iter__和__next__方法, 可以看成是一个迭代器, 有惰性运算的特性,
相对python2提升了性能, 可以节约内存.
(四)内置高阶函数sorted()
sorted(iterable, [,cmp[,key[,reverse]]])
iterable -- 可迭代对象。 cmp -- 比较的函数,这个具有两个参数,参数的值都是从可迭代对象中取出, 此函数必须遵守的规则为,大于则返回1,小于则返回-1,等于则返回0。 key -- 主要是用来进行比较的元素,只有一个参数,具体的函数的参数就是取自 于可迭代对象中,指定可迭代对象中的一个元素来进行排序。 reverse -- 排序规则,reverse = True 降序 , reverse = False 升序(默认)。
二、练习
(一)第一题
给定一个整形数组, 将数组中所有的0移动到末尾, 非0项保持不变;在原始数组上进行移动操作, 勿
创建新的数组;
# 输入:第一行是数组长度, 后续每一行是数组的一条记录;
4
0
7
0
2
# 输出:
调整后数组的内容;
7
2
0
0
def fun(num): if num == 0: return 1 else: return 0 len_num = int(input('数组长度')) list_num = [int(input()) for i in range(len_num)] print(sorted(list_num,key=fun)) #让sorted根据列表的每一个元素进行fun函数操作之后的返回值进行排序,将所有为返回值为1的元素,排在前面
(二)第二题
输入一个只有空格与数字的字符串,将其转化为整形存入列表中,
str_num = input() print(list(map(int,str_num.split()))) #map 将序列的每一个元素一次带入到功能函数中去进行计算,并返回一个map对象
(三)第三题
计算10的阶乘
def jiecheng(x,y): return x*y print(reduce(jiecheng,range(1,11)))
(四)第四题
1. 给定一个列表,li = [[], "hello", " ", "westos", () ]2. 删除列表中的空元素, 包括空列表,空元组, 空集合, 空字典,空字符串;
li = [[], "hello", " ", "westos", () ] def fun(element): if element: return True else: return False print(list(filter(fun,li)))
(五)第五题
1、有下面列表存放的是手机的序号,品牌,销量,价格
info = [
['001', 'apple', 1000, 2],['002', 'xiaomi', 10, 2000],
['003', 'Oppo', 200, 1900],
['004', 'computer', 900, 5000]
]
现根据销量对他们进行升序排序。
info = [ ['001', 'apple', 1000, 2], ['002', 'xiaomi', 10, 2000], ['003', 'Oppo', 200, 1900], ['004', 'computer', 900, 5000] ] def sorted_sales(items): return items[2] print(sorted(info,key = sorted_sales))
2、有下面字典根据每个元素的['count']进行排序
info1 = { '001':{ 'name':'apple', 'count':1000, 'price':2 }, '002': { 'name': 'xiaomi', 'count': 10, 'price': 2000 }, '003': { 'name': 'Oppo', 'count': 200, 'price': 1900 } }
代码:
def return_count(items): return items['count'] print(sorted(info1.values(),key=return_count))
三、匿名函数
1. 匿名函数的关键字为 lambda, 冒号前面是形式参数, 冒号后面是返回值;2. 匿名函数的形式参数可以是: 必选, 默认, 可变, 关键字参数
练习:使用匿名函数实现第二节的1,3,5练习题。
第一题:
len_num = int(input('数组长度')) li=[int(input()) for i in range(len_num)] print(sorted(li,key=lambda x : 1 if x==0 else 0)) #匿名函数方式
第三题:
print(reduce(lambda x,y:x*y,range(1,11)))第五题:
1、
print(sorted(info, key= lambda items : items[2])) #匿名函数方法
2、
print(sorted(info1.values(),key = lambda items : items['count']))
四、返回值是函数名的高阶函数
高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回。
我们来实现一个可变数的求和。通常情况下,求和函数是这样定义的:
def calc_sum(*args): ax = 0 for n in args: ax = ax + n return ax
但是,如果不需要立刻求和,而是在后面代码中,根据需要在计算怎办?我们可以不反回求和的结果,
而是返回求和的函数!
def lazy_sum(*args): def sum(): ax = 0 for n in args: ax = ax + n return ax return sum
当我们调用 lazy_sum()时,返回的并不是求和结果,而是求和函数 。在这个例子中,我们在函数 lazy_sum
中又定义了函数 sum,并且,内部函数 sum 可以引用外部函数,外部函数 lazy_sum 的参数和局部变量,当
lazy_sum 返回函数 sum 时,相关参数和变量都保存在返回的函数中,这种称为“闭包(Closure)”的程序结
构拥有极大的威力。
闭包: 函数里面嵌套函数;
五、装饰器
(一)普通装饰器
由于函数也是一个对象,而且函数对象可以被赋值给变量,所以,通过变量也能调用该函数。当我们需要增强某个函数的功能时,但又不想改变原函数,那么我们可以使用装饰器对函数的功能进行增加。
例如:我们编写一个timeit的装饰器用来装饰函数的执行时间的装饰器。
def timeit(fun): def wrapper(x,y): start_time = time.time() fun(x,y) end_time = time.time() print("%s函数运行时间为%s" % (fun.__name__, end_time - start_time)) return wrapper
装饰器的本质也是一个函数,不过装饰器函数的返回值和它的参数都是一个函数,而且装饰器函数的定义中
还嵌套了一个函数的定义。
如上:当我们的解释器在运行时,def 视为函数定义,则timeit中的内容不会被执行,只有当timeit()函数被调用时,才会执行timeit()的函数体,运行代码。def wrapper() 和return wrapper() 两个语句,并调用wrapper()函数,执行装饰器参数函数fun(),和装饰器的内容。
装饰器在使用时,需要我们在函数定义的前面加上@timeit(@装饰器的名字),例如:
@timeit def hello(): time.sleep(0.04) print("hello") hello()
装饰器的使用语句@timeit 的实质是将 hello = timeit(hello) 这样一个过程,将被修饰的函数的函数名当做实参传入装饰器,并用被装饰的函数的函数名来接收装饰器的返回值。
练习:
1. 创建add_log装饰器, 被装饰的函数打印日志信息;
2. 日志格式为: [字符串时间] 函数名: xxx, 运行时间:xxx, 运行返回值结果:xxx,并自己创建一个函数测结果
import time import functools def add_log(fun): @functools.wraps(fun) # 不会改变原函数的属性; def wrapper(x, y): start_time = time.time() res = fun(x, y) end_time = time.time() #time.ctime() 获取系统字符串时间 print("[%s] 函数名: %s 运行时间:%s 运行返回值结果: %s" % (time.ctime(),fun.__name__, end_time - start_time,res)) return wrapper @add_log def add(x,y): return x+y add(7,5)
结果:
[Mon May 14 15:10:59 2018] 函数名: add 运行时间:1.9073486328125e-06 运行返回值结果: 12
(二)带参数的装饰器
带参数的装饰器:在原来的装饰器外面加上一个函数,用来接收装饰器的参数def addLog(type): def add_log(f): @functools.wraps(f) def wrapper(*args, **kwargs): start_time = time.time() res = f(*args, **kwargs) end_time = time.time() print("日志信息:[%s] [%s] %s 调试方式 %s 运行时间为%.5f s 运行结果为%s" %( type.title(),time.ctime(),type,f.__name__,end_time-start_time,res)) return res return wrapper return add_log调用上面的装饰器
@addLog(type="debug") 传入参数 def add(x,y): time.sleep(random.random()) return x+y
练习:写一个装饰器用来判断函数所接收的参数是不是我们所指定的类型.
import functools import copy def juge_type(*type_tmp): def required_ints(fun): @functools.wraps(fun) def wrapper(*args,**kwargs): for num in args: if not isinstance(num ,type_tmp): type_tmp_list = str(copy.deepcopy(type_tmp)) raise TypeError('参数必须是%s'%type_tmp_list) else: res = fun(*args,**kwargs) return res return wrapper return required_ints
调用:
@juge_type(int,float) def add(x,y): return x+y print(add(4,'4'))
输出:
Traceback (most recent call last): File "/root/PycharmProjects/kaoshi/add_log.py", line 52, in <module> print(add(4,'4')) File "/root/PycharmProjects/kaoshi/add_log.py", line 41, in wrapper raise TypeError('参数必须是%s'%type_tmp_list) TypeError: 参数必须是(<class 'int'>, <class 'float'>)
(三)多个装饰器的执行顺序。
当我们一个函数有两个甚至多个装饰器去修饰的时候,那么我们装饰器的执行顺序是什么呢?
def decorator_a(func): print('Get in decorator_a') def inner_a(*args, **kwargs): print('Get in inner_a') return func(*args, **kwargs) return inner_a def decorator_b(func): print('Get in decorator_b') def inner_b(*args, **kwargs): print('Get in inner_b') return func(*args, **kwargs) return inner_b @decorator_b # f = decorator_b(inner_a) # f -> inner_b @decorator_a # f = decorator_a(f) # f -> inner_a def f(x): print('Get in f') return x * 2 f(1) #f ->inner_b
当我们存在多个装饰器是,在函数定义部分,装饰器的调用顺序是从下至上执行的,如上代码,我们的装饰调用时,先执行的是decorator_a 此时函数名f指向inner_a函数,下来的本质就是讲inner_a函数放到装饰器decorator_b中,使得,函数名f重新指向inner_b ,因此装饰器执行时,又是从上线下的顺序,先执行inner_b ,在执行inner_a函数。
输出:
Get in decorator_a Get in decorator_b Get in inner_b Get in inner_a Get in f
练习:写两个装饰器,一个用来判断用户是否登录,另一个判断当前的用户是不是管理员。
import functools import inspect login_seesion = ['root','student','redhat'] #存放已经登陆的用户的名称的列表 # 该装饰器用来判断当前用户是否为管理员,是才可以添加学生信息 def is_admin(fun): # fun=add_student @functools.wraps(fun) def wapper2(*args, **kwargs): # kwargs = {'name':'root'} #inspect.getcallargs()返回一个字典,key值为:形参 , value值为实参 inspect_name = inspect.getcallargs(fun,*args,**kwargs) print(inspect_name) if inspect_name.get('name') == 'root': temp = fun(*args, **kwargs) return temp else: print("not root/admin user, no permisson add student") return wapper2 #该装饰器用来判断该用户是否登陆成功 def is_login(fun): @functools.wraps(fun) def wapper1(*args,**kwargs): if args[0] in login_seesion: tmp = fun(*args,**kwargs) return tmp else: print('Error!!') return wapper1 @is_login # add_studnet = is_login(wapper1) add_student -> wapper1() @is_admin # add_student = is_admin(add_student) add_student -> wapper2() def add_student(name): #形参:name print('添加学生信息') add_student('root')