装饰器
首先看一个demo:
@func1
def func():
print('aaa')
装饰器存在的意义
- 不影响原有函数的功能
- 可以添加新功能
一般常见的,比如拿到第三方的API接口,第三方不允许修改这个接口。这个时候,装饰器就派上用场。
装饰器本身也是一个函数,作用是为现有存在的函数,在不改变函数的基础上,增加一些功能进行装饰
它是以闭包的形式去实现的。
在使用装饰器函数时,在被装饰的函数的前一行,使用@装饰器函数名
形式来进行装饰。
Demo:
现在在一个项目中,有很多函数,由于我们的项目越来越多,功能也越来越多,导致程序越来越慢。其中一个功能函数的功能,是实现一百万次的累加。
def my_count():
s=0
for i in range(1000001):
s+=i
print('sum:',s)
假如我们要计算函数的运行时间
import time
def my_count():
s=0
for i in range(1000001):
s+=i
print('sum:',s)
start=time.time()
my_count()
end=time.time()
print('运行时间为:',end-start)
sum: 500000500000
运行时间为: 0.06537032127380371
成功的实现时间的运算,但是假如有成千上万个函数,每个函数这么写一遍,非常麻烦,代码量也凭空增加很多
明显不符合开发原则,代码太过冗杂
考虑将上述代码封装成一块在进行操作
import time
def my_count():
s=0
for i in range(1000001):
s+=i
print('sum:',s)
def count_time(func):
start=time.time()
func()
end=time.time()
print('时间:',end-start)
count_time(my_count)
sum: 500000500000
运行时间为: 0.06537032127380371
经过修改后,定义一个函数来实现时间计算功能
初看比之前好很多,只是在使用时需要将我们对应的函数传入到时间计算函数中
但仍然影响了原来的使用
接下来思考,能不能在使用时不影响函数的原来的使用方式,同时进行时间计算
考虑闭包
import time
def count_time(func):
def wrapper():
start = time.time()
func()
end = time.time()
print('时间:', end - start)
return wrapper
def my_count():
s=0
for i in range(1000001):
s+=i
print('sum:',s)
my_count=count_time(my_count)
my_count()
sum: 500000500000
时间: 0.10979533195495605
在使用时,让my_count函数重新指向count_time函数返回的函数引用,这样使用my_count时,就和原来的使用方式一样了
实际就是采用装饰器
import time
def count_time(func):
def wrapper():
start = time.time()
func()
end = time.time()
print('时间:', end - start)
return wrapper
@count_time
def my_count():
s=0
for i in range(1000001):
s+=i
print('sum:',s)
my_count()
sum: 500000500000
时间: 0.12776541709899902
这样实现的好处,定义闭包函数后,只需要通过@装饰器函数名
形式的装饰器语法,就可以将@装饰器函数名
加到要装饰的函数前即可
这种不改变原有函数功,对函数进行拓展的形式,就称为装饰器
在执行@装饰器函数名
时,就是将原函数传递到闭包中,然后原函数的引用指向闭包返回的装饰过的内部函数的引用
装饰器的几种形式
- 无参无返回值
def setFunc(func):
def wrapper():
print('Start')
func()
print('end')
return wrapper
@setFunc # setFunc(show)
def show():
print('show')
show()
Start
show
end
- 无参有返回值
def setFunc(func):
def wrapper():
print('Start')
return func()
print('end')
return wrapper
@setFunc # setFunc(show)
def show():
return 'show'
print(show())
Start
show
- 有参无返回值
def setFunc(func):
def wrapper(s):
print('Start')
func(s)
print('end')
return wrapper
@setFunc # setFunc(show)
def show(s):
print('Hello %s'%s)
show('City')
Start
Hello City
end
- 有参有返回值
def setFunc(func):
def wrapper(x,y):
print('Start')
return func(x,y)
print('end')
return wrapper
@setFunc # setFunc(show)
# def show(s):
# print('Hello %s'%s)
def myAdd(x,y):
return x+y
print(myAdd(2,3))
Start
5
万能装饰器
根据被装饰函数的定义不同,分出了四种形式
能不能实现一种,适用于任何形式函数定义的装饰器
通过可变参数(*args/**kwargs)来接收不同的参数类型
def setFunc(func):
def wrapper(*args,**kwargs):
print('Wrapper context.')
return func(*args,**kwargs)
return wrapper
@setFunc
def func(name,age):
print(name,age)
@setFunc
def fun(a,b,*c,**d):
print(a+b)
print(c)
print(d)
func('Tom',18)
fun(1,2,1999,1,1,school='zucc')
Wrapper context.
Tom 18
Wrapper context.
3
(1999, 1, 1)
{'school': 'zucc'}
函数被多个装饰器所装饰
一个函数在使用时,通过一个装饰器来拓展可能并不能达到预期
一个函数被多个装饰器所装饰
# 装饰器1
def setFunc1(func):
def wrapper1(*args,**kwargs):
print('Wrapper Contxt 1 Start.'.center(40,'-'))
func(*args,kwargs)
print('Wrapper Contxt 1 end'.center(40,'-'))
return wrapper1
# 装饰器2
def setFunc2(func):
def wrapper2(*args,**kwargs):
print('Wrapper Contxt 2 Start.'.center(40,'-'))
func(*args,kwargs)
print('Wrapper Contxt 2 end'.center(40,'-'))
return wrapper2
@setFunc1 #--->F
@setFunc2 #--->g
def show(*args,**kwargs): #--->f
print('show run')
show() #F(g(f))
# 从下往上去装饰
# 从内往外去装饰
--------Wrapper Contxt 1 Start.---------
--------Wrapper Contxt 2 Start.---------
show run
----------Wrapper Contxt 2 end----------
----------Wrapper Contxt 1 end----------
总结:
- 函数可以向普通变量一样,作为函数的参数或者返回值进行传递
- 函数的内部可以定义另外一个函数。目的,隐藏函数功能的实现
- 闭包实际上也是函数定义的一种形式
- 闭包定义的规则,在外部函数内定义一个内部函数,内部函数使用外部函数的变量,并返回内部函数的引用
- Python中,装饰器就是用闭包来实现的
- 装饰器的作用,不改变现有函数的基础上,为函数增加功能
- 装饰器的使用,通过
@装饰器函数名
的形式来给已有函数进行装饰,添加功能 - 装饰器四种形式,根据参数的不同以及返回值的不同
- 万能装饰器,通过可变参数(*args/**kwargs)来实现
- 一个装饰器可以为多个函数提供装饰功能
- 一个函数也可以被多个装饰器所装饰(了解)
- 通过类实现装饰器,重写
__init__
和__call__
函数 - 类装饰器在装饰函数后,原来的引用不再是函数,而是装饰类的对象
静态方法和类方法
静态方法
- 通过装饰器@staticmethod来进行装饰,静态方法既不需要传递类对象也不需要传递实例对象
- 静态方法也可以通过实例对象和类对象去访问
class Dog:
type='狗'
def __init__(self):
name=None
# 静态方法
@staticmethod
def introduce(): #静态方法不会自动传递实例对象和类对象
print('犬科')
dog=Dog()
Dog.introduce()
dog.introduce()
犬科
犬科
静态方法是类中的函数,不需要实例
静态方法主要是用来存放逻辑性的代码,逻辑上属于类,但和类本身没有关系,也就是说,在静态方法中,不会涉及类中属性和方法的操作。
—>可以理解,静态方法是一个独立的单纯的函数,仅仅是托管与某个类的名称空间中,便于维护和管理
使用场景
- 当方法中 既不需要使用实例对象(如实例对象,实例属性),也不需要使用类对象 (如类属性、类方法、创建实例等)时,定义静态方法
- 取消不需要的参数传递,有利于 减少不必要的内存占用和性能消耗
- 如果在类外面写一个同样的函数来做这些事,打乱了逻辑关系,导致代码维护困难,使用静态方法。
类方法
- 类对象所拥有的方法
- 需要用装饰器@classmethod来标识其为类方法
- 对于类方法,第一个参数必须是类对象,一般以cls作为第一个参数
class Dog:
__type='狗'
# 类方法,用class来进行装饰
@classmethod
def get_type(cls):
return cls.__type
print(Dog.get_type())
狗
使用场景
- 当方法中需要使用类对象(如访问私有类属性等)时,定义类方法
- 类方法一般和类属性配合使用
注意:
类中定义了同名的对象方法,类方法以及静态方法时,调用方法会优先执行最后定义的方法
class Dog:
def demo_method(self):
print('对象方法')
@classmethod
def demo_method(cls):
print('类方法')
@staticmethod
def demo_method(): # 最后被定义,调用时优先执行
print('静态方法')
dog=Dog()
Dog.demo_method()
dog.demo_method()
静态方法
静态方法
__call__
方法
引子
def func():
print('hello')
func()
print(func())
# 调用--->call--->报错:不能被call
hello
hello
None
对应的,类实例对象的调用,需要使用到__call__
特殊方法
class student:
def __init__(self,name):
self.name=name
def __call__(self, classmate):
print('我的名字是%s,我的同桌是%s'%(self.name,classmate))
stu=student('aaa')
stu('bbb')
我的名字是aaa,我的同桌是bbb
用类实现装饰器
通过__init__
和__call__
方法实现
class Test:
def __init__(self,func):
print('装饰器准备装饰')
self.__func=func
def __call__(self, *args, **kwargs):
print('Wrapper Context.')
print('before')
self.__func(*args,**kwargs)
print('after')
@Test # 把被装饰的定义当做输入 先进入init再做之后的操作
def show():
print('hello')
print('flag')
show()
装饰器准备装饰
flag
Wrapper Context.
before
hello
after