python基础学习之描述符和装饰器
描述符的了解:
描述符协议:
python描述符是一个“绑定行为”的对象属性,在描述符协议中,它可以通过方法重写属性的访问。这些方法有:
__get__, __set__, 和__delete__
如果这些方法中的任何一个被定义在一个对
象中,这个对象就是一个描述符
总结下说人话:就是当一个类中实例化另一个类的时候,通过本类来调用另一个类的功能,控制实例对象 访问 这个属性做一些额外的操作
class Fee: #先定义一个Fee类
def __get__(self, instance, owner):
print('get:疏楼龙宿人间至帅')
def __set__(self, instance, value):
print('set:疏楼龙宿%s'%value)
def __delete__(self, instance):
print('delete:疏楼龙宿人间无敌')class Fdd: #在定义一个类Fdd
fee = Fee() #我们在Fdd中实例化Fee我们可以通过Fdd实例化来调用Fee中功能,如何实现呢?
a = Fdd() #先实例化,接下来我们需要了解功能的对应调用情况
a.fee # 即调用fee属性,fee属性是什么,就是Fee实例化,这时候会触发__get__功能
a.fee = '人间至帅' #即对fee属性进行修改,会触发类Fee中的__set__功能,将修改内容传入参数value
del a.fee # 删除a实例化中fee属性,就是要删掉Fee(),会触发Fee类中__delete__属性
装饰器:重点啊重点啊重点啊!
什么是装饰器?
装饰器本质上也是函数,他是用来修饰其他函数,给其他函数添加额外功能的
装饰器原则:
1--不改变被修饰函数的源代码
2--不改变被修饰函数的调用方式
装饰器的本质是什么?
高阶函数+函数嵌套+闭包
我们回顾下,什么是高阶函数?
--函数接受的参数是另一个函数的函数名
--函数的返回值另一个函数的函数名
满足上述两个条件中的一个,可以称为高阶函数
【注意:如果返回值是这个函数本身的调用方式,那么这是递归!】
我们再回顾下,什么闭包?
就是函数的返回值是其内部嵌套函数的函数名
按照上述的规则,我们来推导下装饰器应该是什么样子
比如,我们需要测试一下isinstance()这个函数的运行时间
首先,根据原则要求,我们不能改变被修饰函数的源代码,也就是说,isinstance()函数写完后要放在一边,我们先写一个运行代码:
def test_isinstance():
for i in range(0,100000):
if isinstance(i,int) is True:
pass
pass由于一次运行的时间太短,我们运行10W次,
按照原则1,这个时候,这个代码就不能动了,我们应该如何测试他的时间呢?
导入时间模块包,写一段高阶函数,将上述函数传入其中作为参数,在运行前,记录下时间,然后运行该函数,运行完,再记录下时间,做个差运算,得出时间差,即运行时间
import time
def test_time(fun):
star_time = time.time()
fun()
stop_time = time.time()
run_time = stop_time - star_time
print('运行时间是%s' %run_time)OK,我们已经完美的得出了运行时间,而且源代码也没动
那么问题来,函数调用方式,按照test_isinstance()的方式调用,我们没有得到运行时间。。。。。如果要同时得到结果,我们就要使用test_time(test_isinstance)这个方法,和原则2冲突
到目前为止,我们已经解决了需求问题,即测试代码运行时间,那么如果进一步使调用方式不变呢?我们想到了高阶函数中的返回函数名,如果我返回值是需要测试的函数的函数名呢?
代码:
def test_isinstance():
for i in range(0,100000):
if isinstance(i,int) is True:
pass
passimport time
def test_time(fun):
star_time = time.time()
fun()
stop_time = time.time()
run_time = stop_time - star_time
print('运行时间是%s' %run_time)return test_isinstance
这个时候,我们再做如下操作:
test_isinstance = test_time(test_isinstance)
问题似乎解决了,我直接使用就可以得到结果
至此,我们已经得到了 装饰器的雏形,至于最后一部复制问题,装饰器的格式,可以解决,又称为 语法糖
@test_time
def test_isinstance():
.......
.......
最开的时候@test_time的功能效果即是:
test_isinstance = test_time(test_isinstance)
仔细检查后,发现我们似乎运行了两次test_isinstance函数?
test_time中运行了一次,接受返回函数名后我们加上括号,又运行了一次,问题来了,这下怎么办?
遇到问题,解决问题,多运行了一次,我们看看能不能少运行一次
常规手段无法达成目的,我们回忆下装饰器的的本质是什么
高阶函数(已有)+函数嵌套(无)+闭包(无)
我们还有其他手段可以用,比如函数嵌套,和闭包
我们要实现的终极目的是test_isinstance()的同时会让上述的两个函数都运行一次,且只运行一次,而且,要获得原函数结果,上述test_time函数已经可以获得时间,同时喊运行了一次test_isinstance函数,如果我们截取下他内部test_isinstance的运行结果,作为本函数的返回值呢?
import time
def test_time(fun):
star_time = time.time()
res = fun()
stop_time = time.time()
run_time = stop_time - star_time
print('运行时间是%s' %run_time)return res
我们可以发现,运行这个函数,我们可以得到和运行test_isinstance函数一样的结果,因为返回值就是他的运行结果
我们似乎离真相越来越近了
如同上面说的一样,我们需要test_isinstance()的时候,让函数运行一次,而不是两次,函数运行是加括号直接运行,少运行一次,我们就要少一个括号,即test_isintance的时候不运行,test_isintance上面我们等于了 test_time(test_istntance),也即是,这个时候,我不运行函数,等加上括号的时候,在运行函数,兼顾上述我们测试出来所需要的所有功能【增加附加功能,获取本函数原返回值】
经过思考和测试,使用闭包功能
import time
def test_time(fun):
def test():
star_time = time.time()
res = fun()
stop_time = time.time()
run_time = stop_time - star_time
print('运行时间是%s' %run_time)
return res
return test解释代码:
在测试函数上挂上@test_time,即:test_isintance = test_time(test_isintance),这个时候,同等于test_isintance=test【因为装饰器函数test_time的返回值是test,test为内嵌函数】;当test_isintance加上括号()运行的时候,等于运行了test()然后会进行下面一系列操作,同时,会返回原函数的返回值。
至此,装饰器函数出炉了。
增加问题:
如果我函数本身就有参数呢。。。。。。。
比如:test_isintance(1,2,3,4,5)
这个等于什么?不就等于 test(1,2,3,4,5) 么!
也就是说我内嵌函数中,有可能会有参数!代码,需要普适性,这种情况也需要考虑进去!参数形式能确定么?似乎不能,同样,我们也不应该根据测试函数的参数特点再去修改函数。
我们需要把参数传给,那么函数参数形参和实参的赋值关系主要分为哪几类?
位置参数、关键字参数,即*args **kwargs,我们在test_isintance的括号内加上参数,也就是在test的括号内加上参数,可能是0个,可能是若干个,*args和**kwargs完美接收了上述参数,在原本的函数test_isintance()的参数传给test()后,再由test在代码块内部传给test_isintance()【即代码中的fun()】
最后我们的代码应该如下:
import time
def test_time(fun):
def test(*args,**kwargs):
star_time = time.time()
res = fun(*args,**kwargs)
stop_time = time.time()
run_time = stop_time - star_time
print('运行时间是%s' %run_time)
return res
return test至此,装饰器完全体
使用方式:在上述代码已经提前写好的情况下,使用语法糖即可
@test_time
def test_isinstance():
for i in range(0,100000):
if isinstance(i,int) is True:
pass
pass
最新文章
- HDU 4251 The Famous ICPC Team Again 主席树
- 转载:XPath基本语法
- leetcode 125. Valid Palindrome ----- java
- xml学习笔记一(概述)
- 《University Calculus》-chaper13-多重积分-三重积分的引入
- hibernate 对 sql server 2005 分页改进
- [j2ee][IDEA properties中文乱码解决]
- win10 安装 mysql解压版安装步骤
- centos yum install redis
- firefox在引入vue.js后不支持e=e||window.event的解决办法
- dijkstra算法:寻找到全图各点的最短路径
- CentOS随笔——Service与防火墙关闭
- mskitten
- android使用ARouter跳转activity(阿里巴巴开源的)
- cyberduck的SSH登录
- Bar-Code-Recognition-System Private
- noip第16课作业
- P4878 [USACO05DEC]layout布局
- Linux ftp命令的使用方法 -- 转
- Android开发15——给TextView加上滚动条