高阶函数
可以接受其他方法作为参数的函数称为高阶函数。
这个还是因为Python的变量名不是固定一成不变的,不像Java、C#需要直接指明该变量的类型后就无法改变。通过多次画内存图我们可以得知,变量名只是一个指针,它可以随时指向任何类型的变量,也包括可以指向一个函数。
下面我们用代码尝试一下:
def addNum(x,y):
return x +y
x = 3
y = 5
print(addNum(x,y))
#把addNum方法赋值给了_f,那么_f现在就是指向此函数的,可以把它当作函数调用
#可以仿照C++ typedef关键字的理解,理解为给函数去了一个别名
_f = addNum
print(_f(x,y))
输出:
C:/Users/ZBZ/PycharmProjects/untitled/excise03.py
8
8
Process finished with exit code 0
那么既然这样,按照高阶函数的定义,把函数当作变量传进来,其实在前面函数式编程讲继承与多态的时候我们已经应用了。我们换个例子,比如现在有三个方法,一个求平方的、一个求乘法的】,让使用这两个方法写一个求aX^2的方法,代码如下:
def sqrNum(x):
return x * x
def mulNum(x,y):
return x * y
def doublesqr(a,x,sqr,mul):
return mul(a,sqr(x))
#把addNum方法赋值给了_f,那么_f现在就是指向此函数的,可以把它当作函数调用
#可以仿照C++ typedef关键字的理解,理解为给函数去了一个别名
x = 10
a = 5
s = sqrNum
m = mulNum
print(doublesqr(a,x,s,m))
输出结果:
500
Process finished with exit code 0
讲完了这个再记录下几个常用的Python内置高阶函数:
- filter:根据传入的方法贵族筛选、自动过滤掉不符合条件的元素并且返回符合条件的可迭代对象。注意看下每个返回值的类型:
class skill:
def __init__(self,name,atk):
self.__name = name
self.__atk = atk
@property
def atk(self):
return self.__atk
@atk.setter
def atk(self,value):
self.__atk = value
def __str__(self):
return "此技能名为%s伤害为%s"%(self.__name,self.__atk)
skills = []
skills.append(skill("九阴真经",99))
skills.append(skill("降龙十八掌",95))
skills.append(skill("蛤蟆功",85))
for item in filter(lambda item:item.atk >90, skills):
print(type(item))
print(item)
输出结果:
<class '__main__.skill'>
此技能名为九阴真经伤害为99
<class '__main__.skill'>
此技能名为降龙十八掌伤害为95
Process finished with exit code 0
- map:返回筛选后的结果,不一定是原来的可迭代对象,返回新的可迭代对象。
class skill:
def __init__(self,name,atk):
self.__name = name
self.__atk = atk
@property
def atk(self):
return self.__atk
@atk.setter
def atk(self,value):
self.__atk = value
def __str__(self):
return "此技能名为%s伤害为%s"%(self.__name,self.__atk)
skills = []
skills.append(skill("九阴真经",99))
skills.append(skill("降龙十八掌",95))
skills.append(skill("蛤蟆功",85))
for item in map(lambda item:item.atk, skills):
print(type(item))
print(item)
for item in map(lambda item:(item.atk +5)*2, skills):
print(type(item))
print(item)
输出结果:
<class 'int'>
99
<class 'int'>
95
<class 'int'>
85
<class 'int'>
208
<class 'int'>
200
<class 'int'>
180
- max:求最大值
print(max(skills,key = lambda item:item.atk))
输出:
此技能名为九阴真经伤害为99
Process finished with exit code 0
从闭包到装饰器
假如上面做的技能,有一个做好的攻击Attack(),后来觉得不合理,需要在Attack()之前有一个释放的状态,那么该如何做呢?代码向下面:
# m = mulNum
class skill:
def __init__(self,name,atk):
self.__name = name
self.__atk = atk
@property
def atk(self):
return self.__atk
@atk.setter
def atk(self,value):
self.__atk = value
def __str__(self):
return "此技能名为%s伤害为%s"%(self.__name,self.__atk)
def Attack(self):
print("用%s造成了%s点伤害"%(self.__name,self.__atk))
skills = []
skills.append(skill("九阴真经",99))
skills.append(skill("降龙十八掌",95))
skills.append(skill("蛤蟆功",85))
for Skill in skills:
Skill.Attack()
输出:
用九阴真经造成了99点伤害
用降龙十八掌造成了95点伤害
用蛤蟆功造成了85点伤害
Process finished with exit code 0
闭包这个知识点就是为了装饰器的实现,而装饰器的作用就是解决此类问题。
闭包有三个要素才称为闭包:
- 必须有一个内嵌函数
- 内嵌函数必须引用外部函数中的变量
- 必须返回内嵌函数
觉得仅仅这么说,不好理解,直接上代码看内存图。
def travel():
aim = "上海"
def go():
nonlocal aim
aim = "坐车去" + aim
print(aim)
print(aim)
return go
a = travel()
a()
输出结果:
上海
坐车去上海
这就组成了一个闭包。然后再来看一下内存图:
闭包最重要的就是一个整合的作用,把内嵌函数和它外面一层的函数资源的整合。这样调用外部函数的时候,返回的go()函数赋值给了变量a,当a调用执行的时候,也就是go()执行完毕后资源才会释放。这样最后一个大的红色框体框起来的,包含了内嵌函数、外部函数整体资源的,就成为一个闭包。
装饰器
下面叙述一个场景,假如做一个英雄,有四个技能,然后英雄根据技能别人,类设计如下:
class skill:
def __init__(self,name,atk,supernatural):
self.__name = name
self.__atk = atk
self.__supernatural = supernatural
@property
def name(self):
return self.__name
@property
def atk(self):
return self.__atk
@atk.setter
def atk(self,value):
self.__atk = value
@property
def supernatural(self):
return self.__supernatural
def __str__(self):
return "此技能名为%s伤害为%s"%(self.__name,self.__atk)
def Attack(self):
print("用%s造成了%s点伤害"%(self.__name,self.__atk))
class Hero:
supernatural = 700
def __init__(self,name):
self.__name = name
self.__skills = []
self.__skills.append(skill("九阴真经", 99, 100))
self.__skills.append(skill("降龙十八掌", 65, 50))
self.__skills.append(skill("蛤蟆功", 85, 30))
def xiangLongShiBaZhang(self):
for _skill in self.__skills:
if(_skill.name == "降龙十八掌"):
_skill.Attack()
def JiuYinZhenJing(self):
for _skill in self.__skills:
if(_skill.name == "九阴真经"):
_skill.Attack()
def HaMaGong(self):
for _skill in self.__skills:
if(_skill.name == "蛤蟆功"):
_skill.Attack()
h = Hero("慕容复")
h.HaMaGong()
h.xiangLongShiBaZhang()
h.JiuYinZhenJing()
输出:
用蛤蟆功造成了85点伤害
用降龙十八掌造成了65点伤害
用九阴真经造成了99点伤害
现在要加一个新的需求,想调用攻击方法的时候,检查蓝条是否足够释放并且增加是谁攻击的播报。下面就要用到装饰器了:
class Hero:
supernatural = 700
def __init__(self,name):
self.__name = name
self.__skills = []
self.__skills.append(skill("九阴真经", 99, 100))
self.__skills.append(skill("降龙十八掌", 65, 50))
self.__skills.append(skill("蛤蟆功", 85, 30))
def xiangLongShiBaZhang(self):
for _skill in self.__skills:
if(_skill.name == "降龙十八掌"):
_skill.Attack()
def JiuYinZhenJing(self):
for _skill in self.__skills:
if(_skill.name == "九阴真经"):
_skill.Attack()
def HaMaGong(self):
for _skill in self.__skills:
if(_skill.name == "蛤蟆功"):
_skill.Attack()
def UseSkill(self,_skill):
print(self.__name,"释放技能")
return _skill
h = Hero("慕容复")
HaMaGong = h.UseSkill(h.HaMaGong)
HaMaGong()
JiuYinZhenJing = h.UseSkill(h.JiuYinZhenJing)
JiuYinZhenJing()
xiangLongShiBaZhang = h.UseSkill(h.xiangLongShiBaZhang)
xiangLongShiBaZhang()
输出:
慕容复 释放技能
用蛤蟆功造成了85点伤害
慕容复 释放技能
用九阴真经造成了99点伤害
慕容复 释放技能
用降龙十八掌造成了65点伤害
在上面的类中使用Usekill方法,通过使用闭包的概念,对每次释放技能之前添加释放者播报。这是最基础的方法对调用原来的方法进行拦截。这样的缺点是,调用的时候太过麻烦。再来个过渡版,解决调用问题。由于写的类使用属性拦截执行时报错,下面的举例参考教学视频里的视频暂时使用函数式编程,暂时不用类。代码如下:
def verify_permissions(func):
def wrapper(*args, **kwargs):
print("权限验证")
func(*args, **kwargs)
return wrapper
# 已有功能
@verify_permissions
def enter_background(login_id, pwd):
print(login_id, pwd, "进入后台")
@verify_permissions
def delete_order(id):
print("删除订单", id)
enter_background("abc", 1234)
delete_order(101)
这是最终的版本,包括了带不通传参的装饰器构建。