Python学习笔记11:函数修饰符

Python有很多有趣的特性,其中函数修饰符就是一个。

我们在之前的那个web应用示例中用过如下写法:

@web.route('/log')

@符号后边的,就是一个函数修饰符,它可以在不改变原有函数的情况下改变函数的行为(通常来说是增强函数的行为)。

我们下面就来说说怎样实现一个函数修饰符。

在这之前,我们要先介绍几个必须要先掌握的内容。

接收和返回函数

如我们在Python学习笔记0:变量中讨论的,在Python中,所有的一切都是对象,而函数也不例外。那理所应当的,函数也可以作为对象被另一个函数所接收和返回。

我们看下边的例子:

def receiveFunc(func):print(type(func))return funcdef hellow():print("hellow world")returned=receiveFunc(hellow)
returned()

输出

<class ‘function’>
hellow world

我们可以看到,receiveFunc接收了函数hellow并打印出其类型,然后返回,返回的函数依然可以正常执行并输出结果。

可变参数列表

我们都知道,在函数签名的定义中,参数数目可以是一个,也可以是多个,但都是定义的时候就指定的,但Python还有另一种定义方法,可以接收数目不定的参数。

def getMultipParam(*multipParams):for param in multipParams:print(param,' ',end='')print()getMultipParam(1,2,3)
getMultipParam('a','b','c','d','e')

输出

1 2 3
a b c d e

通过类似*params这种方式,可以指定参数接收的是可变长度的参数列表,这种方式有点像C++中的指针,可以接收一个数组作为参数,而在Python这里,就是接收一个列表,本质上接收函数会把收到的参数列表转变为一个列表进行处理。

当然,你也可以直接传一个列表过去,但是需要用*来指定:

def getMultipParam(*multipParams):for param in multipParams:print(param,' ',end='')print()getMultipParam(*[1,2,3])
getMultipParam(*['a','b','c','d','e'])

输出

1 2 3
a b c d e

可以看出两者是等价的。但是这么做是有意义的,其目的在于要用*来说明传过去的列表是作为可变参数列表来传递,而非是仅仅一个普通参数。当然,如果不这么做接收函数就会认为仅仅传入了一个参数,是个列表。

到这里,关于可变参数的内容说完了吗?并没有。

如果大家还记得,参数传递还有另一种方式,即指明参数名称,进行一对一的传递。如果是那种方式,可变参数要如何定义呢?

def getKVParams(**kvParams:dict):for paramKey,paramVal in kvParams.items():print(paramKey,':',paramVal,end=',')print()getKVParams(name="jack",age=16)
getKVParams(career="enginner",age=18)

输出

name : jack,age : 16,
career : enginner,age : 18,

和之前的定义类似,不过是将传入参数作为字典来处理,相应的,也同样可以直接把可变参数列表作为字典来传入:

def getKVParams(**kvParams:dict):for paramKey,paramVal in kvParams.items():print(paramKey,':',paramVal,end=',')print()getKVParams(**{"name":"jack","age":16})
getKVParams(**{"career":"teacher","age":17})

输出

name : jack,age : 16,
career : teacher,age : 17,

你以为到这里就结束了?

太天真,我们还可以把这两者结合起来!

def getEveryParams(*multipParams,**kvParams:dict):for param in multipParams:print(param,end=',')print()for paramKey,paramVal in kvParams.items():print(paramKey,':',paramVal,end=',')print()getEveryParams(1,2,3,age=16,name="jack")

输出:

1,2,3,
age : 16,name : jack,

可以看到,通过将两种方式结合,我们的函数就可以接收任何形式的传参了,至于这有什么用,不用急,看完这篇博客就能明白了。

关于前置知识,还有最后一个,内部函数。

内部函数

相信对Java不陌生的朋友应该会在此时说,这个我会,内部类!

不错,Python的内部函数在我看来和Java的内部类颇为类似,而且事实上两者也是一致的,因为我一直所强调的,在Python里,一切都是对象,所以Python会存在和Java内部类高度类似的东西也不足稀奇。

def getInnerFunc():def innerFunc():print("hellow world")return innerFunctest=getInnerFunc()
test()

输出

hellow world

可以看到,getInnerFunc函数的内部定义了一个内部函数,然后将这个函数返回,而外部程序拿到返回的函数以后也可以正常执行。而这种使用方式也恰恰是内部函数的最常用方式。

函数修饰符

好了,终于要讲到我们的主题了,如何构造一个我们自己的函数修饰符。

其实其核心要点就是把我们之前讲的三种技巧结合起来。

我们始终要有这样的概念,函数修饰符的作用是将一个已有的函数增强其用途。

这种思想可以用现实的例子来类比,就拿我喜欢的军事来说吧。现在的主战坦克在真正作战中肯定不会裸奔,需要按作战行为增加诸多附加模块,比如悬挂附加装甲,增加自动反导弹装置,还有眼目发生器和红外干扰装置之类的更是稀松平常。

但是这些最重要的是什么,是我们在没有改变坦克本身的情况下增强了坦克的作战性能。

是不是很酷?

我们现在就在Python中这么做吧。

我们先定义一个坦克:

def tank(player1:'驾驶员',player2:'装填手',player3:'车长',player4:'炮长'):print("这是一个裸奔的坦克")print("该坦克有四名成员")

别问为什么这里没有用英语。。。

我们现在用坦克工厂给它加装反应装甲:

def tank(player1:'驾驶员',player2:'装填手',player3:'车长',player4:'炮长'):print("这是一个裸奔的坦克")print("该坦克有四名成员")def tankFactory(tank:function)->function:def powerTank():tank()print("加装反应装甲")print("加装反导弹装置")print("加装红外干扰仪")return powerTank

可以看到,我们利用上边说的两个特性来构建了一个坦克工厂,来增强进入工厂的坦克。

等等,我们是不是忘掉了什么,对,我们这里忘记了使用可变参数,我们需要注意到,上边的示例是有问题的,比如从工厂返回的powerTank居然没有参数,这就相当于这个增强版坦克顶盖被焊死了,不能进驾驶员,这显然是不合理的,没有部队会接受这样的改装。

那我们像tank的定义一样指定四个成员作为参数行不行?当然是可以的,但是这同样有问题,比如现在一些先进坦克是自动装填,不需要炮手,只要三个成员,那你这工厂就不能接受这种坦克了,这相当有局限性。

所以,这里就是我们的可以接受任何类型参数的技巧的作用所在了,让我们改进一下我们的坦克工厂:

def tankFactory(tank:"function")->"function":def powerTank(*params:tuple,**kvParams:dict):tank(*params,**kvParams)print("已加装反应装甲")print("已加装反导弹装置")print("已加装红外干扰仪")return powerTank@tankFactory
def tank(player1:'驾驶员',player2:'装填手',player3:'车长',player4:'炮长'):print("这是一个裸奔的坦克")print("该坦克有四名成员")@tankFactory
def mordenTank(player1,player2,player3):print("这是一个3成员的现代坦克")tank("tom","jerry","robot1","robot2")
mordenTank("tom","jerry","robot")

输出:

这是一个裸奔的坦克
该坦克有四名成员
已加装反应装甲
已加装反导弹装置
已加装红外干扰仪
这是一个3成员的现代坦克
已加装反应装甲
已加装反导弹装置
已加装红外干扰仪

可以看到,我们现在的坦克工厂非常棒,通过在要改装的坦克函数前简单的加上@tankFactory就可以直接改装。真是太方便了。

等等,在你准备撸袖子改装掉所有类型的坦克前,还需要注意一点,因为Python中所有函数都是对象,而作为函数修饰符的函数也不例外,而解释器在处理这种特殊的函数时,有时候会忘记这是一个函数修饰符。所以我们需要显示地告诉Python解释器,这是一个作为函数修饰符的特殊函数,而非普通货色。

当然,这有点奇怪,但我们要做的是接受它。

至于如何做,很简单:

functools模块引入一个函数wraps,并在函数修饰符中调用这个函数。

from functools import wraps
def tankFactory(tank:"function")->"function":@wraps(tank)def powerTank(*params:tuple,**kvParams:dict):tank(*params,**kvParams)print("已加装反应装甲")print("已加装反导弹装置")print("已加装红外干扰仪")return powerTank@tankFactory
def tank(player1:'驾驶员',player2:'装填手',player3:'车长',player4:'炮长'):print("这是一个裸奔的坦克")print("该坦克有四名成员")@tankFactory
def mordenTank(player1,player2,player3):print("这是一个3成员的现代坦克")tank("tom","jerry","robot1","robot2")
mordenTank("tom","jerry","robot")

这就是最终的代码了。

关于函数修饰符的内容已经讲完了,但其实还有一些扩展性的问题可以讨论,比如函数修饰符这种叫法很怪异和别扭,其实叫做修饰器更妥当,因为其本质就是设计模式中的修饰器模式。还有函数修饰符和我们前边讲的上下文模式也颇为类似,是不是可以用函数修饰符实现一个上下文模式?

诸如此类。

但是因为今天忙了别的,已经有点晚了,就将这些内容和下一篇笔记一起发吧。

晚安。

Python学习笔记11:函数修饰符相关推荐

  1. Python学习笔记12_函数

    Python学习笔记12_函数 文章目录 Python学习笔记12_函数 1.函数定义 2.函数调用 3.函数的参数 3.1.可更改对象和不可更改对象参数 3.2.必需参数(位置参数) 3.3.关键字 ...

  2. Python学习笔记:函数(Function)

    Python学习笔记:函数(Function) 一.函数基本概念 函数是Python里组织与重用代码最重要的方法.一般来说,如果你期望多次重复相同或相似的代码,写一个可重用的函数可能是值得的.函数通过 ...

  3. Python学习笔记——一些函数

    本文对应头歌上的Python练习:https://www.educoder.net/paths/pn7qklv9 基础知识1: input( )函数 input()函数从控制台获得用户输入,无论用户在 ...

  4. 小甲鱼python003答案_小甲鱼:Python学习笔记003_函数

    >>> # 函数 >>> def myFirstFunction(params1,params2...): print("这是我的第一个函数!" ...

  5. Python学习 :面向对象 -- 成员修饰符

    成员修饰符 两种成员 - 公有成员 - 私有成员, __字段名 - 无法直接访问,只能通过内部方法来间接访问私有成员 简例:公有成员与私有成员  class Info:country = '中国' # ...

  6. java中哪些可以私有化_《Java基础学习笔记》JAVA修饰符之私有化(Private)

    1,什么是private修饰符? private是权限修饰符,用于修饰类中的成员(成员变量,成员函数). private修饰后的成员只在本类中有效. /* 例: * 将age私有化以后,类以外即使建立 ...

  7. python函数参数学习_python学习笔记-11.函数参数和返回值进阶

    1. 函数参数和返回值的作用 函数根据有没有参数以及有没有返回值,可以相互组合,共有4种形式: 无参数,无返回值 无参数,有返回值 有参数,无返回值 有参数,有返回值 定义函数时,是否接收参数,或者是 ...

  8. python学习笔记三一 函数学习

    函数学习 range函数 •生成有序的序列 •生成数字队列可以定制 # range函数案例1 # 生成一个从1到10的数字序列 # range的生成序列的两个面数字是左包括,右不包括(如下所示只包括1 ...

  9. Python学习笔记--day09 函数 模块 (文件、路径操作)

    第二模块 函数&模块 第一模块主要是学习python基础知识,从第二模块开始就可以通过程序去解决工作中实际的问题. 函数,一个用于专门实现某个功能的代码块(可重用). 内置函数 len.bin ...

最新文章

  1. AI语音独角兽思必驰完成数亿元Pre-IPO融资,冲刺苏州AI创业第一股
  2. mysql所有表查询
  3. jdk 1.8 java.policy,JDK1.8 导致系统报错:java.security.InvalidKeyException:illegal Key Size
  4. 飞鸽传书内部排序算法的性能比较
  5. ESXI主机的Management网络管理了什么?
  6. 分页 Paginator
  7. AI算法工程师 | 01人工智能基础-快速入门
  8. echarts 热力图(中国地图版)
  9. CMS网站页面管理开发汇总
  10. winhex使用教程_[攻略] 无限视距——上帝视角教程
  11. 考研英语 - word-list-44
  12. 当国际贸易撞上AI,会产生怎样的化学反应?
  13. 盘点SCI、SSCI、EI……的前世今生
  14. 巨简单又好用的pygame游戏暂停继续方法
  15. ubuntu 安装软件 tar.gz deb
  16. 山东高新技术企业认定流程详解
  17. 2 C++标准库(1- IO库、顺序容器和泛型算法)
  18. 好牛逼的技术——Android运行时Crash自动恢复框架:Recovery
  19. Python网络与并发编程 10 threading模块线程锁
  20. TensorFlow模型保存pb或ckpt

热门文章

  1. ERR_ACTION_ACCESS_UNDEFINED: Are you trying to access this.someMutation() or this.someGetter inside
  2. 2023年南京大学软件学院专硕上岸经验帖
  3. Could not determine own NN ID in namespacenvclusterPleaseensurethatthisthe machineslistedRPC
  4. HNU程序设计训练 斯诺克台球(屑题)
  5. 7个副业赚钱的野路子,看懂你也能赚钱?
  6. GTC22 | NVIDIA 为设计师和创作者推出全新 Ada Lovelace RTX GPU
  7. 【Golang】GOOROOT/GOPATH/GOBIN
  8. php larval框架运行环境,4种Windows系统下Laravel框架的开发环境安装及部署方法详解...
  9. 指纹识别-(5)指纹图像预处理算法之图像方向场
  10. python入门教程 - 滑块实战[附源码]