装饰器的使用方法
装饰器是什么?当程序函数写好之后业务做了修改,此时需要更改原来的函数。为了简化此操作,即不再改变原函数,python提供了装饰器机制。
同时也可以避免大量的代码重复,例如有三个函数f1,f2,f3分别需要计算三个函数的执行时间,如下所示:
from time import time, sleep
def f2():
start = time()
for i in range(100):
eval('1 + 1 * 2')
sleep(1)
end = time()
print(f'f2执行时间为{end - start}')
def f3():
start = time()
for i in range(50):
eval('2 + 1 / 3')
sleep(3)
end = time()
print(f'f3执行时间为{end - start}')
def f4():
start = time()
for i in range(25):
eval('4 + 1 + 5')
sleep(2)
end = time()
print(f'f4执行时间为{end - start}')
if __name__ == '__main__':
f2()
f3()
f4()
需要将代码段
start = time()
...
end = time()
print(f'执行时间为{end - start}')
写三次,当函数很多时,显然会降低编码效率,因此我们引入了装饰器,顾名思义,就是对一个函数进行“装饰”。
无参数的装饰器
# coding=utf-8
def decorate(func):
def wrapper():
print(f'函数{func.__name__}开始执行...')
func()
print(f'函数{func.__name__}执行完毕')
return wrapper
@decorate
def f1():
print('计算1+1的结果')
@decorate
def f2():
print('计算2+2的结果')
f1()
f2()
在函数f1执行时,会把f1函数作为参数传到装饰器decorate中,然后返回decorate中的函数wrapper,实际执行的是wrapper函数(wrapper函数中的func函数会执行f1函数)
以上代码的输出结果为:
函数f1开始执行...
计算1+1的结果
函数f1执行完毕
函数f2开始执行...
计算2+2的结果
函数f2执行完毕
到此,我们可以通过装饰器修改以上计算运行时间的代码段
# coding=utf-8
from time import time, sleep
def decorate(func):
def wrapper():
start = time()
func()
end = time()
print(f'{func.__name__}执行时间为{end - start}')
return wrapper
@decorate
def f2():
for i in range(100):
eval('1 + 1 * 2')
sleep(1)
@decorate
def f3():
for i in range(50):
eval('2 + 1 / 3')
sleep(3)
@decorate
def f4():
for i in range(25):
eval('4 + 1 + 5')
sleep(2)
if __name__ == '__main__':
f2()
f3()
f4()
以上代码的执行结果依然为:
f2执行时间为1.0111098289489746
f3执行时间为3.00311541557312
f4执行时间为2.0160653591156006
带固定参数函数的装饰器
# coding=utf-8
def decorate(func):
def wrapper(number):
print(f'{func.__name__}函数开始执行...')
func(number)
print(f'number的值为{number}')
print(f'{func.__name__}函数运行结束\n')
return wrapper
@decorate
def f1(number):
print(f'计算{number}+{number}的值...')
@decorate
def f2(number):
print(f'计算{number}*{number}+{number}的值...')
f1(1)
f2(2)
输出结果为
f1函数开始执行...
计算1+1的值...
number的值为1
f1函数运行结束
f2函数开始执行...
计算2*2+2的值...
number的值为2
f2函数运行结束
带不定参数函数的装饰器
# coding=utf-8
def decorate(func):
def wrapper(*args, **kwargs):
print(f'{func.__name__}函数开始执行...')
func(*args, **kwargs)
print(f'{func.__name__}函数运行结束\n')
return wrapper
@decorate
def f1(number1, number2):
print(f'计算{number1}+{number2}的值...')
@decorate
def f2(number1, number2, number3):
print(f'计算{number1}*{number2}+{number3}的值...')
f1(1, 2)
f2(2, 3, 4)
输出结果为
f1函数开始执行...
计算1+2的值...
f1函数运行结束
f2函数开始执行...
计算2*3+4的值...
f2函数运行结束
带参数的装饰器
# coding=utf-8
def decorate(style):
def wrapper(func):
def wrapped(*args, **kwargs):
print(f"装饰器的参数为{style}")
print(f"{func.__name__}函数开始执行...")
func(*args, **kwargs)
print(f'{func.__name__}函数执行完毕\n')
return wrapped
return wrapper
@decorate(style='invis')
def f1(num, cnt):
print(f"num={num}, cnt={cnt}")
@decorate(style='bold')
def f2(x, y, z):
print(f"x={x}, y={y}, z={z}")
f1(1, 2)
f2(3, 4, 5)
输出结果为
装饰器的参数为invis
f1函数开始执行...
num=1, cnt=2
f1函数执行完毕
装饰器的参数为bold
f2函数开始执行...
x=3, y=4, z=5
f2函数执行完毕
类作为装饰器
类作为装饰器时要实现类的__init__方法和__call__方法
不带参数
不带参数的装饰器中的__init__方法要传入函数
# coding=utf-8
class Decorate:
def __init__(self, func):
self.func = func
def __call__(self, *args, **kwargs):
print(f'{self.func.__name__}函数开始执行...')
self.func(*args, **kwargs)
print(f'{self.func.__name__}函数执行完毕\n')
@Decorate
def f1(x, y):
print(f"x={x}, y={y}")
@Decorate
def f2(x, y, z):
print(f'x={x}, y={y}, z={z}')
f1(1, 2)
f2(3, 4, 5)
输出结果为
f1函数开始执行...
x=1, y=2
f1函数执行完毕
f2函数开始执行...
x=3, y=4, z=5
f2函数执行完毕
带参数
带参数的装饰器中的__init__方法传入参数,__call__方法传入函数
# coding=utf-8
class Decorate:
def __init__(self, style):
self.style = style
def __call__(self, func):
def wrapper(*args, **kwargs):
print(f'装饰器参数值为{self.style}')
print(f'{func.__name__}函数开始执行...')
func(*args, **kwargs)
print(f'{func.__name__}函数执行完毕\n')
return wrapper
@Decorate(style='invis')
def f1(x, y):
print(f"x={x}, y={y}")
@Decorate(style='bold')
def f2(x, y, z):
print(f'x={x}, y={y}, z={z}')
f1(1, 2)
f2(3, 4, 5)
输出结果为
装饰器参数值为invis
f1函数开始执行...
x=1, y=2
f1函数执行完毕
装饰器参数值为bold
f2函数开始执行...
x=3, y=4, z=5
f2函数执行完毕
类内的成员函数作为类内函数的装饰器使用
# coding=utf-8
class Test:
def __init__(self, num):
self.num = num
def decorate(func):
def wrapper(self, *args, **kwargs):
print(f'{func.__name__}函数开始执行...')
func(self, *args, **kwargs)
print(f'{func.__name__}函数执行完毕\n')
return wrapper
@decorate
def standard_output(self, x, y, z):
print(f'x={x}, y={y}, z={z}')
s = Test(2)
s.standard_output(3, 4, 5)
输出结果为
standard_output函数开始执行...
x=3, y=4, z=5
standard_output函数执行完毕
类的成员函数作为类外函数的装饰器
# coding=utf-8
class Test:
def __init__(self, num):
self.num = num
def decorate(func):
def wrapper(*args, **kwargs):
print(f'{func.__name__}函数开始执行...')
func(*args, **kwargs)
print(f'{func.__name__}函数执行完毕\n')
return wrapper
@Test.decorate
def standard_output(x, y, z):
print(f'x={x}, y={y}, z={z}')
standard_output(3, 4, 5)
输出结果为
standard_output函数开始执行...
x=3, y=4, z=5
standard_output函数执行完毕
python内置装饰器
property和setter,deleter
先看这么一个例子
# coding=utf-8
class Student:
def __init__(self, name, sex):
self.name = name
self.sex = sex
self.info = f'姓名:{self.name}, 性别:{self.sex}'
def modify_sex(self):
self.sex = '男'
s = Student(name='刘能', sex='女')
print(s.info)
s.modify_sex() # 此时性别变为男
print(s.info) # 性别输出仍为女
输出结果为
姓名:刘能, 性别:女
姓名:刘能, 性别:女
虽然改变了性别,但是因为没有改变info,此时输出结果不会变
当然,可以采用下列方法,在修改sex的同时修改info
def modify_sex(self):
self.sex = '男'
self.info = f'姓名:{self.name}, 性别:{self.sex}'
但是程序已经成形时,在每一地方添加以上代码会耗费大量的时间,
因此我们可以把一个成员方法看作是一个变量,即使用property装饰器
# coding=utf-8
class Student:
def __init__(self, name, sex):
self.name = name
self.sex = sex
#self.info = f'姓名:{self.name}, 性别:{self.sex}'
def modify_sex(self):
self.sex = '男'
@property
def info(self):
return f'姓名:{self.name}, 性别:{self.sex}'
s = Student(name='刘能', sex='女')
print(s.info)
s.modify_sex()
print(s.info)
输出结果为
姓名:刘能, 性别:女
姓名:刘能, 性别:男
显然,这种方法是只读的,即不能根据info函数修改对象的成员变量,所以我们可以引入setter装饰器
# coding=utf-8
class Student:
def __init__(self, name, sex):
self.name = name
self.sex = sex
self._info = f'姓名:{self.name}, 性别:{self.sex}'
@property
def info(self):
return self._info
@info.setter
def info(self, student_info):
if not isinstance(student_info, str):
raise TypeError('类型错误')
self._info = student_info
s = Student(name='刘能', sex='女')
print(s.info)
s.info = '姓名: 刘不能, 性别: 未知'
print(s.info)
输出结果为
姓名:刘能, 性别:女
姓名: 刘不能, 性别: 未知
staticmethod装饰器
staticmethod方法一般其参数与类本身、类对象无关,通过类名或者对象名调用即可
# coding=utf-8
class Dog:
num = 0
def __init__(self, name='lisa'):
self.name = name
Dog.num += 1
@staticmethod
def print_num():
print(f'num={Dog.num}')
def __del__(self):
Dog.num -= 1
a = Dog()
b = Dog()
a.print_num()
del a
Dog.print_num()
输出结果为
num=2
num=1
classmethod装饰器
classmethod是类的方法,因为python不像c++一样可以重载,则可以用classmethod装饰器实现重载,类似于普通类方法中的self,classmethod中有关键字cls
# coding=utf-8
class Time:
def __init__(self, second, minute, hour):
self.second = second
self.minute = minute
self.hour = hour
@classmethod
def from_string(cls, s):
second, minute, hour = list(map(eval, s.split(':')[:3]))
return cls(second, minute, hour)
@classmethod
def from_default(cls):
return cls(0, 0, 0)
def __str__(self):
return '%02d:%02d:%02d' % (self.hour, self.minute, self.second)
a = Time(11, 22, 12)
print(a)
b = Time.from_default()
print(b)
c = Time.from_string('59:59:23')
print(c)
输出结果为
12:22:11
00:00:00
23:59:59