高阶函数
高阶函数需满足条件:
- 函数名 作为 参数传入, 内置高阶函数map,filter,reduce
- 函数名 作为 返回值 匿名函数作为返回值
- 高阶函数是函数式编程的体现。函数式编程就是指这种高度抽象的编程范式。
高阶函数求绝对值
def sum_num(a, b, f):
return f(a) + f(b)
result = sum_num(-4, 6, abs)
print(result) # 10
内置高阶函数
map(function, iterable,…)
function:函数;iterable:一个或多个序列
map(),将传入的函数变量 作用到序列变量的每个元素中,并将结果组成新的迭代器返回,需要进行强转。
list1 = [1, 2, 3, 4, 5]
def func(x):
return x ** 2
result = map(func, list1)
print(result) # <map object at 0x000001E4B87C9828>
print(list(result)) # [1, 4, 9, 16, 25]
reduce(function, iterable[, initializer])
function – 函数,有两个参数;iterable – 可迭代对象;initializer – 可选,初始参数
reduce(func, lst),其中 func 必须有两个参数。每次 func 计算的结果继续和序列的下一个元素做累积计算。
import functools
lis1 = [1, 2, 3, 4, 5]
def func(a, b):
return a + b
res = functools.reduce(func, lis1) # 1和2相加的值,跟3继续相加,依次类推
print(res) # 15
filter(function, iterable)
function – 判断函数;iterable – 可迭代对象。
函数用于过滤序列,过滤掉不符合条件的元素,返回一个 filter 对象,可以用list()转为列表。
lis1 = [11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
def func(x):
return x % 2 == 0
res = filter(func, lis1)
print(res) # <filter object at 0x00000253D7AC75C0>
print(list(res)) # [12, 14, 16, 18, 20]
闭包
闭包的定义需要满足以下三个条件:
- 在一个外函数中定义了一个内函数
- 内函数里运用了外函数的临时变量
- 并且外函数的返回值是内函数的引用
代码从上到下执行,第一步在test(20)出处调用了test函数,打印了“–1--”,继续往下执行,test_in函数有被调用,到打印“–3--”,返回test_in到test的函数调用处,此时的res等同于test_in,res(25)相当于给test_in传递参数25,进入test_in函数的调用,打印“–2--”,返回 number_in + number到test_in的调用处,用res1接收,打印res1。
def test(number): # number 形参 临时变量
print("--1--")
def test_in(number_in):
print(number_in)
print('--2--')
return number_in + number
pass
print("--3--")
return test_in
# test()执行完毕后,输出1,3。并且函数里面的变量(number)会被释放掉
res = test(20) # res = test_in
res1 = res(25) # test_in()
print(res1) # 45,说明外层函数执行完毕后,number没有被释放掉,而是继续给内层函数使用
"""--1--,--3--,25,--2--,45。闭包是一种现象
总结:一般情况下,外层函数执行完毕之后,临时变量会被释放掉,但是此时
外层函数发现自己的临时变量会在将来被使用,在外层函数结束的时候,在返回内层函数的引用的同时,
将外层函数的临时变量与内层函数进行绑定"""
装饰器
例1.计算test1运行时间
import time
def test1():
start_time = time.time() # 当前时间
print("--1--")
time.sleep(2) # 程序运行太快,相减会输出为 0,暂停2秒,再减掉
end_time = time.time() # 当前时间
print(f"程序运行时间:{end_time - start_time -2}")
test1()
如果要测试test2,test3等等多个函数的时间,复制粘贴的话,会造成大量代码的冗余。可以把用于计算时间的函数单独封装
import time
def test_all(fun):
start_time = time.time()
fun()
end_time = time.time()
print(f"程序运行时间:{end_time - start_time - 2}")
def test1():
print("--1--")
time.sleep(2)
def test2():
print("--2--")
time.sleep(2)
test_all(test1)
test_all(test2)
将公共的代码提出来,封装成函数,然后以传参的时候传递函数。
装饰器
装饰器 (@) 是一种语法糖,主要用于在函数或类的基础上添加一些可重用的额外功能。从应用开发的角度来看的话,我们使用装饰器来对我们的应用代码进行功能性扩展和包装,以提高代码的可重用性和可维护性。不改变原有的代码的基础上,为函数添加新的功能。
由于我们在开发中要遵守封闭开放原则(实现的代码块尽量不做修改,可以拓展新的功能),所以python开发者开始使用装饰器,装饰器也就是说在不改变源代码的情况下为函数添加新的功能。并且使用@符号,@符号为语法糖。
应用场景:
# 需要在各个函数前增加打印“hello world”
def pri_hel(f):
print('hello world!')
return f
@pri_hel # 装饰器,@+装饰器函数的名称。相应于test1的返回函数 test = f = test1
# 相当于test1 =pri_hel(test),在下面函数没有调用的时候,以及提前执行了
def test():
sum_li = sum([12, 3, 5])
print(sum_li)
return sum_li # 可以省略
test1 =pri_hel(test) # test函数作为实参传递给形参f,此时 test=f=test1,此行代码相当于@pri_hel
test1() # f返回函数的调用处 test1,再调用test1,此时test1() = f() = test(), 运行结果是 hello world 19
'''代码从上至下执行,到pri_hel(test)时,将test函数的引用(函数名)传入进去,test相当于f,打印hello world,把f返回到函数的调用处(test1),用test1来接收,test1就是f本身,test1()调用的是test函数,相当于在执行test函数前,执行了hello world'''
# 如果用test1 =pri_hel(test()),相当于f=test()=19,则说明在传参之前以及调用过依次test()。会先打印一次test()的结果19,得到的结果是 19 hello world 19
引入装饰器
def pri_hel(f):
print('hello world!')
return f
@pri_hel # 装饰器,@+装饰器函数的名称。相应于 test = f
def test():
sum_li = sum([12, 3, 5])
print(sum_li)
return sum_li
test()
运行的结果是 :
hello world
19
def pri_hel(f):
print('hello world!')
return f
@pri_hel # 相当于test =pri_hel(test),在下面函数test()没有调用的时候,装饰器就已经提前执行了
def test():
sum_li = sum([12, 3, 5])
print(sum_li)
return sum_li
@pri_hel # 相当于test2 =pri_hel(test)
def test2():
print("我爱学习") # 注意,test和test2函数在没有调用的时候,装饰器就已经执行,两个hello world会先被打印
test()
test2()
运行的结果:
hello world!
hello world!
20
我爱学习
如果把test(),test2()注释掉,也会直接打印两个hello world 。test()和test2()函数未被调用时,装饰器就已经开始执行了。注意装饰器与下面的函数在书写时不要留有空行。
例1的代码优化
# 有问题的代码,重复调用了test1()
import time
def test_all(func): # func = test1
start_time = time.time()
func() # 相当于test1()
end_time = time.time()
print(f"程序运行时间:{end_time - start_time - 2}")
return func
def test1():
print("--1--")
time.sleep(2)
test =test_all(test1)
test()
**'也可以写为'**
import time
def test_all(func): # func = test1
start_time = time.time()
func() # 相当于test1()
end_time = time.time()
print(f"程序运行时间:{end_time - start_time - 2}")
return func
@test_all
def test1():
print("--1--")
time.sleep(2)
test1()
返回的结果是
--1--
程序运行时间:0.0061604976654052734
--1--
代码从上至下执行,执行到test_all(test1),相当于把test1作为实参传入到 test_all(func),在函数内执行func(),调用了 test1,打印 ‘–1--’,然后计算结束时间,此时返回的func就是test1,返回到函数的调用处,此时 test = func = test1,最后用test()进行调用的时候,相当于 test1()执行了2次。
下面解决重复打印’–1--‘的问题
import time
def test_all(func): # func = test1
# 满足了闭包的条件
def test_in(): # 1.外函数里面有内函数
start_time = time.time()
func() # 相当于test1() # 2.内函数引用了外函数的临时变量
end_time = time.time()
print(f"程序运行时间:{end_time - start_time - 2}")
return test_in # 3.内函数的引用作为外函数的返回值
def test1():
print("--1--")
time.sleep(2)
test = test_all(test1) # test = test_in
test() # 相当于调用了 test_in
返回的结果为
--1--
程序运行时间:0.0036284923553466797
程序从上至下执行,先执行 test_all(test1),将test1作为实参传递给func,test_all函数不会执行,返回 test_in 给函数的调用处 test,此时test = test_in,然后test() 相当于调用了 test_in(),然后去执行函数体内的内函数test_in(),此时func的函数名也是个变量名,func相当于临时变量,执行了test1函数的内容,然后执行print后,结束运行。
也可以写为
import time
def test_all(func): # func = test1
# 满足了闭包的条件
def test_in(): # 1.外函数里面有内函数
start_time = time.time()
func() # 相当于test1() # 2.内函数引用了外函数的临时变量
end_time = time.time()
print(f"程序运行时间:{end_time - start_time - 2}")
return test_in # 3.内函数的引用作为外函数的返回值
@test_all
def test1():
print("--1--")
time.sleep(2)
test1()
返回的结果为
--1--
程序运行时间:0.000457763671875
在上面两个程序中,@test_all就相当于 test = test_all(test1) test()
运行过程中形成了闭包的现象,1.嵌套函数,2.内层函数引用外层函数的变量(函数名本质也是变量),3.内层函数的引用作为外层函数的返回值。
推导式
Python 中有一种特有的语法,又称为解析式。推导式是可以从一个数据序列构建另一个新的数据序列的结构体。
共有三种推导:列表(list)推导式;字典(dict)推导式;集合(set)推导式
列表推导式
例2.
生成一个[0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5, 6.0, 6.5, 7.0, 7.5, 8.0, 8.5,9.0, 9.5, 10.0] 的列表。
切记:所有的步长均不得为浮点数。不得写range(1,11,0.5),会报错
li = []
for i in range(1,21):
# print(i/2)
li.append(i/2)
print(li)
可以用列表推导式用简短的代码实现。
列表推导式1.
[out_express for out_express in input_list]
方括号里面for…in 的结构,for里面的表达式内容在in后的表达式里,for左边的是要添加到新的列表里的内容。
lis = [i/2 for i in range(1, 21)]
print(lis)
仅用2行代码实现刚才5行代码所显示的内容。
例3
有列表如:li = [6, 2, 6, 7, -15, 8, -17, -10, -15, -4]。需求:将li列表 <0 的数进行平方生成新列表。共三种方法
方法一:
li = [6, 2, 6, 7, -15, 8, -17, -10, -15, 4]
li1 = []
for i in li:
if i < 0:
i = i ** 2
li1.append(i)
print(li1)
方法二:
# 将小于0的用filter过滤出来,然后用map映射做平方。
li = [6, 2, 6, 7, -15, 8, -17, -10, -15, 4]
lis = list(filter(lambda x:x<0,li))
print(list(map(lambda x:x**2,lis)))
列表推导式2
[out_express for out_express in input_list if out_express_condition]
for…in…if…结构,不是三目运算符([True_statements if expression else False_statement]),if后的条件如果满足,就执行for前面的语句,切记,千万不要加上else。
方法三:列表推到式
li = [6, 2, 6, 7, -15, 8, -17, -10, -15, 4]
lis1 = [i**2 for i in li if i < 0]
print(lis1)
例4
for i in '123':
for j in 'abc':
print(i+j)
可以用列表推导式来简化代码。
列表推导式3
推导式的嵌套
[i for row in matrix for i in row],可以往后一直嵌套
print([i+j for i in '123' for j in 'abc'])
字典推导式
{out_exp_key: out_exp_value for out_exp in input_list}
例5
实现以下列表中的元素与索引,形成{key:value}的形式
li = ['age', 'name', 'weight','gender']
print({i:li.index(i) for i in li})
集合推导式
{out_exp_res for out_exp in input_set}
例6
生成10个1-100之间的元素,并进行去重。
import random
print({random.randint(1, 100) for i in range(10)})
因为去重了,所以得到的值可能不会到10个。
注意:元组没有推导式,而是生成器。可以通过tuple()进行强转。
tu = (i for i in range(4))
print(tu) # 生成器,<generator object <genexpr> at 0x0000026D7AD01C50>
print(type(tu)) # 生成器,<class 'generator'>
print(tuple(tu)) # (0, 1, 2, 3),强转为tuple类型
练习
1.过滤掉该列表names = [“jerry”,“hansen”,“Amy”,“Wendy”,“Tom”,“Bob”]把长度小于或等于3的字符串列表,并将剩下的转换成大写字母。
names = ["jerry", "hansen", "Amy", "Wendy", "Tom", "Bob"]
print([i.upper() for i in names if len(i) >3])
2.求(x,y),其中x是0-5之间的偶数,y是0-5之间的奇数组成的元组列表。
效果如下:[(0, 1), (0, 3), (2, 1), (2, 3), (4, 1), (4, 3)]
第一种:
print([(x, y) for x in range(0, 5, 2) for y in range(1, 5, 2)])
第二种:
print([(x, y) for x in range(5) if x % 2 == 0 for y in range(5) if y % 2 == 1])
3.[ ‘Bob’,‘JOHN’,‘alice’,‘bob’,‘ALICE’,‘James’,‘Bob’,‘JAMES’,‘jAMeS’ ]
该列表很紊乱,实现去重,以及将名字格式统一成首字母大写(str.capitalize())
lis = [ 'Bob','JOHN','alice','bob','ALICE','James','Bob','JAMES','jAMeS' ]
print({i.capitalize() for i in lis})