装饰器
装饰,顾名思义就是在原来的基础上进行美化及完善,器这里指函数,所以说装饰器就是装饰函数,也就是在不改变原来函数的代码及调用方式的前提下对原函数进行功能上的完善。其核心原理其实是利用闭包格式 @关键字+装饰函数
被装饰函数()
注意:@行必须顶头写而且是在被装饰函数的正上方
按照形式可以分为:无参装饰器和有参装饰器,有参装饰器即给装饰器加上参数
引入:想要在原来的函数里增加新的功能,需要用到嵌套函数
想要在nanbei这个函数里增加新的功能,可以再次定义一个函数boom,这样修改了原来的代码,会增加出错的概率,那怎么办呢?
可以在外面动态修改结果:
def nanbei():
return '南北,' #原函数
def boom():
return nanbei() + '你真是一位好老师哦~'
print(boom())
#运行结果:
南北,你真是一位好老师哦~
但希望还是使用nanbei()这个函数名,不用boom()
def nanbei():
return '南北,' #原函数
def boom(func):#括号里传入的是nanbei()这个函数
return func() + '你真是一位好老师哦~'
nanbei = boom(nanbei)
print(nanbei)
#运行结果:
南北,你真是一位好老师哦~
但是还是不太一样,本来应该是print(nanbei()),现在是print(nanbei),继续:
def nanbei():
return '南北,' #原函数
def boom(func):#括号里传入的是nanbei()这个函数
def new_func():
return func() + '你真是个好老师哦~'
return new_func
nanbei = boom(nanbei)
print(nanbei())
#运行结果:
南北,你真是个好老师哦~
上面的boom就是一个装饰器
以上是装饰器的原理
下面是装饰器的用法:
def boom(func):
def new_func():
return func() + '你真是个好老师哦~'
return new_func
@boom #相当于 nanbei = boom(nanbei),如果不用@boom,就要在函数后面写nanbei = boom(nanbei)
def nanbei():
return '南北,' #原函数
print(nanbei())
@符号是装饰器的语法糖,在定义函数的时候使用
注意: 时刻牢记 @boom只是一个语法糖,其本质是nanbei = boom(nanbei)
其他装饰器:
(1)@property:像访问属性一样访问方法
class Rectangle:
def __init__(self, length, width):
self.length = length
self.width = width
# 像访问属性一样访问方法
@property
def get_area(self):
return self.width *self.length
rec1= Rectangle(50,30)
print(rec1.get_area)#不加@property装饰器,调用方法的时候需要加括号,即print(rec1.get_area())
(2)@staticmethod:静态方法
class Rectangle:
def __init__(self, length, width):
self.length = length
self.width = width
def fun1():
print('我没有self')
Rectangle.fun1()
这样看上去没有什么问题,也能运行,但其实这段代码在pycharm中会有波浪线提示,有瑕疵。这时就需要加上@staticmethod这个装饰器,表明fun1这个函数和我们的类没有多大关系
class Rectangle:
def __init__(self, length, width):
self.length = length
self.width = width
@staticmethod
def fun1():
print('我没有self')
Rectangle.fun1()
(3)@classmethod:类装饰器
class Rectangle:
def __init__(self, length, width):
self.length = length
self.width = width
@classmethod
def show(cls): #cls代表类本身 如果加上self在调用时要把实例传入
print(cls)
print('show fun')
Rectangle.show()
#运行结果:
<class '__main__.Rectangle'>
show fun
注:@staticmethod不能访问类属性和类方法,@classmethod可以访问类属性 但都可以用类名访问class Rectangle(object):
name= '我是一个长方形' #定义一个类变量
def __init__(self, length, width):
self.length = length
self.width = width
@staticmethod
def fun1():
#print(name) #会报错,@staticmethod不能访问类属性和类方法
print('我没有self')
@classmethod
def show(cls): #cls代表类本身 如果加上self在调用时要把实例传入
print(cls)
print(cls.name)
print('show fun')
#Rectangle.fun1() #会报错,@staticmethod不能访问类属性和类方法
Rectangle.show()
#运行结果:
<class '__main__.Rectangle'>
我是一个长方形
show fun
类装饰器:类可以做装饰器,但必须定义__call__方法
class TestClass(object):
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
print('1234')
return self.func()
@TestClass
def shuzi():
print('5678')
shuzi()
# 运行结果
1234
5678
装饰器参考:
from datetime import datetime
def run_time(func):
def new_func(*args,**kwargs):
start_time = datetime.now()
print('程序开始时间:%s' % start_time)
func(*args, **kwargs)
end_time = datetime.now()
print('程序结束时间:%s' % end_time)
print('程序总共执行时间:%s' % (end_time - start_time))
return new_func()
练习:
测试type和isinstance两个函数,那个速度更加的快
from datetime import datetime
from time import sleep
def run_time(func):
def new_func(*args,**kwargs):
start_time = datetime.now()
print('程序开始时间;%s' % start_time)
a = func(*args,**kwargs)
end_time = datetime.now()
print('程序结束时间:%s' % end_time)
print('程序总共执行时间:%s'% (end_time - start_time))
return a
return new_func
@run_time
def test_time1(*args):
return type(*args)
print(test_time1('啊哈哈哈'))
sleep(2)
print('')
@run_time
def test_time2(*args):
return isinstance(*args,str)
print(test_time2('kmkl'))
#运行结果:
程序开始时间;2018-04-15 02:12:37.799030
程序结束时间:2018-04-15 02:12:37.817031
程序总共执行时间:0:00:00.018001
<class 'str'>
程序开始时间;2018-04-15 02:12:39.870149
程序结束时间:2018-04-15 02:12:39.879149
程序总共执行时间:0:00:00.009000
True
由此可见,isinstance的效率比type的效率高