一、什么是高阶函数
1.1、高阶函数的概念和作用:
高阶函数是指
接受函数作为参数
或者
返回函数
作为结果的函数。它在函数式编程中是一个重要概念(函数式编程(Functional Programming
,
FP
)是一 种编程范式,它将计算视为数学函的求值,并且避免使用可变数据结构和 改变状态的操作。函数式编程的核心思想是将函数作为程序的基本构建块, 强调不可变性和纯函数的使用)。
1.2、高阶函数的特点:
(1)、接受一个或多个函数作为输入参数:这意味着你可以将函数作为参数传 递给另一个函数。
(2)、返回一个函数作为输出:这意味着函数可以返回另一个函数。
二、当参数为一个函数的情况
案例:
#
需求:定义一个函数,带两个整数类型的参数
#
该函数的功能是把两个
参数整理之后
做求和计算,并把结果返回
非高阶函数的写法:
def my_function(num1,num2):
# 对传入的两个参数进行平方处理
new_num1 = num1**2
new_num2 = num2**2
# 对处理后的文件进行求和操作,并返回
return new_num1+new_num2
print(my_function(3,4))
非高阶函数写法的弊端:
由于题目的需求的不确定性,针对于“参数整理之后”没有明确的规定,在上面的案例中,我们对传入的两个参数进行了平方再求和的操作,但是实际上有些时候,我们不同的用户调用函数时可能进行不同的处理,比如张三要对这两个数字求绝对值再求和呢?就不得吧求平方的代码改为求绝对值的代码,这样的话函数的用途就很有限了。那么我们怎么样设计才能够不管调用者想对他进行什么操作,而不用改变函数体内的代码呢,当然可以,这个时候我们就需要使用函数作为参数实现了。
高阶函数的写法:
1、当行为函数是自定义时:
# 定义行为函数
def do_function(number):
return number * 10
# 注意:在参数中传入函数时不需要加(),加上小括号,表示的是调用函数,不是参数
def my_function(num1,num2,function):
"""
:param num1: 传入第一个数字的参数
:param num2: 传入第二个数字的参数
:param function: 传入你想要进行的函数操作
:return: 返回值是对传入的数字进行处理后求和计算
"""
return function(num1) + function(num2)
# 调用函数:调用函数在传入函数时不需要加(),加上小阔号表示的是调用函数,不是传参
print(my_function(1,2,do_function))
2、当行为函数是内置模块的函数时:
def my_function(num1,num2,function):
"""
:param num1: 传入第一个数字的参数
:param num2: 传入第二个数字的参数
:param function: 传入你想要进行的函数操作
:return: 返回值是对传入的数字进行处理后求和计算
"""
return function(num1) + function(num2)
# 导入math模块进行先求根再进行操作
import math
# 对参数进行求平方根再求和
print(my_function(100, 4, math.sqrt))
# 对参数进行阶乘操作后求和
print(my_function(2, 4, math.factorial))
3、传入的函数只有一句话时:可以使用lambda表达式:
# 刚刚求和的函数:
my_function = lambda num1, num2,function: function(num1) + function(num2)
# 行为参数:
do_function = lambda number: number * number
# 调用函数并输出
print(my_function(1, 2, do_function))
4、总结:
(1)、当定义函数时,函数的参数是一个函数,那么定义的这个函数可以称为 高阶函数。
(2)、调用高阶函数时需要特别注意,由于高阶函数中参数类型是个函数,因此在进行调用时,我们只需要传递函数的名字即可,而不是进行函数调用。
(3)、函数作为参数的类型时,可以是内置的函数,如abs
,也可以是其它模块 下的函数,如sqrt
,也可以是自定义的函数,如
num_num
,像上面代码中 num_num函数的函数体只有一行代码,可以用
lambda
表达式的方式写。
5、当参数有两个函数时:
# 传入math模块
import math
# 定义高阶函数
def self_fuc(num1,num2,function1,function2):
# 对第一个参数乘10,对第二个参数阶乘操作,再求和
return function1(num1) + function2(num2)
# 调用函数:传入两个行为函数
print(self_fuc(1, 2, lambda x: x * 10, math.factorial))
三、当返回值是一个函数的时候
def out_function(*args):
print("我是外部函数!")
def inner_function(number):
sum = 0
for i in args:
sum += i
print("我是内部函数里面的number,传入的值为:",number)
return f'累加结果为:{sum}'
# 返回值为内部函数,不需要加(),加上表示调用函数
return inner_function
# 返回为地址:因为返回的内部函数的名字,打印的为地址
print(out_function(2,3,4))
# 外部函数返回为内部函数的inner_function,后面的括号表示的inner_function(666)
print(out_function(2,3,4)(666))
四、常见的Python内置高阶函数
4.1、map函数
map()
函数
接受一个函数
和
一个可迭代对象
作为参数,将函数应用于可迭代 对象的每个元素,并
返回一个新的迭代器
。
参数
1
:要传入进行处理的函数名
参数
2
:一个可以操作的数据集或者序列或者是可迭代对象
简单点可以理解为:把参数
2
中的每个元素拿出来,放到参数
1
的函数中去处 理一遍,再返回到一个新的数据集中去。
案例:
# 导入math模块
import math
# 定义一个可迭代数据
numbers = [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
# 调用高阶函数map()并且传入参数,使用sqrt函数对每个数据求算数平方根
map_res = map(math.sqrt, numbers)
print(map_res)
# 由于map函数返回是一个迭代器对象,直接打印的话不够直观
# 因此使用list函数给它进行转化一下输出更直观
print(list(map_res))
4.2、reduce函数
reduce()
函数位于
functools
模块中,它接受
一个函数
和一个
可迭代对
象 作为参数,
函数必须接受两个参数
。reduce()
会将函数依次应用于可迭代 对象的元素,
返回一个单一的结果
。
# 导入包
import functools
def my_fc(x,y):
print("x的值:",x)
print("y的值:",y)
return x + y
# 定义要操作的可迭代数据
my_list = [1,2,3,4,5]
# 调用reduce函数
res = functools.reduce(my_fc,my_list)
print(res)
4.3、filter函数
filter()
函数接受一个函数和一个可迭代对象作为参数。
filter()
会
过滤
掉那些函数返回False
的元素,返回一个新的迭代器
# 定义行为参数
def my_function(x):
if x > 10:
return x
# 定义要处理的可迭代数据
num_list = [1,2,3,4,5,10,20,30,40,50]
# 调用filter函数
res = filter(my_function, num_list)
print(list(res))
4.4、sorted函数
sorted()
函数可以对任何可迭代对象进行排序,并返回一个新的列表。它接受一个key
参数,该参数可以是一个函数,用于指定排序的依据。
stu = [
{"name" : "jack","age" : 20},
{"name" : "tom","age" : 19},
{"name" : "lucy","age" : 19},
]
# 先根据年龄排序,如果年龄相同,再根据名字排序
new_stu = sorted(stu, key=lambda x:(x["age"],x["name"]),reverse=True)
print(new_stu)
4.5、any函数和all函数
any()
:如果可迭代对象中至少有一个元素为
True
,则返回
True
。
all()
:如果可迭代对象中所有元素都为
True
,则返回
True
。
numbers = [1,2,3,4,5,0]
# 是否有非0元素:
is_true = any(numbers)
print(is_true)
# 是否没有0元素
is_true1 = all(numbers)
print(is_true1)
4.6、zip函数
zip()
函数接受多个可迭代对象作为参数,将它们组合成一个迭代器,每个元素是一个元组,包含来自每个可迭代对象的对应元素。
names = ("李小妹","帅哥","张三","tom")
ages = [23,24,20]
# 组合成一个列表
res = list(zip(names,ages))
print(res)
# 组合成一个字典
res1 = dict(zip(names,ages))
print(res1)
# 组合成一个元组
res2 = tuple(zip(names,ages))
print(res2)
# 如果合成后还有一个可迭代对象的元素没有匹配,那么将会自动舍弃
4.7、enumerate函数
enumerate()
函数接受一个可迭代对象和一个可选的起始索引,返回一个迭代器,每个元素是一个元组,包含索引和对应的值。
names = ["李小妹","帅哥","张三","tom"]
res = enumerate(names,start=0)
# 只能转化为list
print(list(res))
print(dict(res))
print(tuple(res))
print(set(res))
五、高阶函数的练习
假设你是一家电商公司的数据分析师,需要对销售数据进行分析。你有一个 包含多个订单信息的列表,每个订单信息是一个字典,包含订单ID
、客户ID、订单金额和订单日期。你需要完成以下任务:
1.
筛选出订单金额大于
1000
元的订单
。
2.
计算每个订单的折扣金额(折扣率为
10%
)
。
3.
计算所有订单的总金额
。
4.
找出订单金额最高的订单
。
任务要求:
1.
筛选出订单金额大于
1000
元的订单
:
使用
filter
函数筛选出订单金额大于
1000
元的订单。
将结果存储在变量
high_value_orders
中。
2.
计算每个订单的折扣金额(折扣率为
10%
)
:
使用
map
函数计算每个订单的折扣金额。
将结果存储在变量
discounted_orders
中,每个订单字典中增加
一个
discount
键,值为折扣金额。
3.
计算所有订单的总金额
:
使用
map
函数提取所有订单的金额。
使用
reduce
函数计算所有订单的总金额。
将结果存储在变量
total_amount
中。
4.
找出订单金额最高的订单
:
使用
max
函数和
key
参数找出订单金额最高的订单。
将结果存储在变量
highest_amount_order
中。
from functools import reduce
# 数据
orders = [
{"order_id": 1, "customer_id": 101, "amount": 1200,"date": "2024-01-01"},
{"order_id": 2, "customer_id": 102, "amount": 800,"date": "2024-01-02"},
{"order_id": 3, "customer_id": 103, "amount": 1500,"date": "2024-01-03"},
{"order_id": 4, "customer_id": 104, "amount": 950,"date": "2024-01-04"},
{"order_id": 5, "customer_id": 105, "amount": 1800,"date": "2024-01-05"}
]
# 1. 筛选出订单金额大于1000元的订单
high_value_orders = list(filter(lambda order:
order["amount"] > 1000, orders))
# 2. 计算每个订单的折扣金额(折扣率为10%)
discounted_orders = list(map(lambda order: {**order,
"discount": order["amount"] * 0.1}, orders))
# 3. 计算所有订单的总金额
amounts = list(map(lambda order: order["amount"], orders))
total_amount = reduce(lambda x, y: x + y, amounts)
# 4. 找出订单金额最高的订单
highest_amount_order = max(orders, key=lambda order:
order["amount"])
# 输出结果
print("订单金额大于1000元的订单:", high_value_orders)
print("每个订单的折扣金额:", discounted_orders)
print("所有订单的总金额:", total_amount)
print("订单金额最高的订单:", highest_amount_order)
六、递归函数的引入
如果一个函数在执行过程中调用自身,那么这个函数就可以称为递归函数。 也就是在函数体中调用自己。
递归函数的特点:
1
、函数体中调用自己
2
、有一个条件作为出口。不然一直自己调用自己层层套下去就成死循环 了,所以必须要有个条件终止。
阶乘案例:、
def fbnq(n):
if n == 1:
return 1
else:
return n * fbnq(n-1)
print(fbnq(4))