一、故事背景
石原里美在学习排序算法时看到了这个博客python 实现几大排序算法,里面提到了不同的算法排序的时间复杂度不同,于是想通过实验来感受一下。直接通过计算不同算法的排序时长,感受时间复杂度。于是乎,写了个冒泡排序。
def bubble_sort():
nums = []
for i in range(9999):
nums.append(random.random())
n = len(nums)
for i in range(n):
for j in range(1,n-i):
if nums[j] < nums[j-1]:
nums[j],nums[j-1] = nums[j-1],nums[j]
return nums
添加计时代码段:
def bubble_sort():
nums = []
for i in range(9999):
nums.append(random.random())
t1 = time() #起始时间节点
print('begin bubble sort')
n = len(nums)
for i in range(n):
for j in range(1,n-i):
if nums[j] < nums[j-1]:
nums[j],nums[j-1] = nums[j-1],nums[j]
print('after bubble sort, cost time:',time()-t1) #结束时间节点
return nums
一个一个这样写很麻烦,需要把每个排序函数里面的代码都添加计时代码段,导致很麻烦,后面如果不想要计时了又需要删除。于是乎,找你来帮忙,要用优雅的方式来解决。
二、装饰器
所谓装饰器,本质就是一个函数,而返回值也是一个函数。它可以在不改动代码的情况下增加额外的功能,比如计时、打印日志、权限校验等。那么,按照上面的思想,我们可以实现一个装饰器,用来计时的。也就是将函数作为参数,传入到计时函数中运行。
def bubble_sort():
nums = []
for i in range(999):
nums.append(random.random())
n = len(nums)
for i in range(n):
for j in range(1,n-i):
if nums[j] < nums[j-1]:
nums[j],nums[j-1] = nums[j-1],nums[j]
return nums
def get_time(func):
def inner():
t1 = time()
print('begin execute')
result = func()
print('{} execute time is {} s'.format(func.__name__,time()-t1))
return inner
getTime = get_time(bubble_sort)
getTime()
#begin execute
#bubble_sort execute time is 0.0748293399810791 s
看下get_time这个函数,传入的参数是一个函数,返回也是一个函数,运行之后获打印排序的时长。但是,这样写需要从新进行声明,每次用起来很麻烦。
注意: 这里get_time中的函数 inner()其实是一个闭包,所谓闭包可以理解为引用了函数外定义的变量(inner中的func就是调用get_time传入的func参数)
三、语法糖
语法糖是什么鬼?语法糖就是通过@符号就可以完成以上装饰器的使用,并且不需要进行函数的声明,直接调用原函数即可完成装饰器的功能。
def get_time(func):
def inner():
t1 = time()
print('begin execute')
result = func()
print('{} execute time is {} s'.format(func.__name__,time()-t1))
return inner
@get_time
def bubble_sort():
nums = []
for i in range(999):
nums.append(random.random())
n = len(nums)
for i in range(n):
for j in range(1,n-i):
if nums[j] < nums[j-1]:
nums[j],nums[j-1] = nums[j-1],nums[j]
return nums
bubble_sort()
# begin execute
# bubble_sort execute time is 0.07283329963684082 s
其中@get_time的作用就是把bubble_sort当做参数传入,即get_time(bubble_sort)
四、传参、返回值
那么如果get_time需要传参咋办?这里就需要把装饰器外面再加上一层了,最外层可以接收get_time的参数,内层接收函数参数。
def get_time(msg=None):
print(msg)
def execute_time(func):
def inner():
t1 = time()
print('begin execute')
result = func()
print('{} execute time is {} s'.format(func.__name__,time()-t1))
return inner
return execute_time
@get_time('this is decorator')
def bubble_sort():
nums = []
for i in range(999):
nums.append(random.random())
n = len(nums)
for i in range(n):
for j in range(1,n-i):
if nums[j] < nums[j-1]:
nums[j],nums[j-1] = nums[j-1],nums[j]
return nums
bubble_sort()
# this is decorator
# begin execute
# bubble_sort execute time is 0.07383942604064941 s
如果bubble_sort需要传递参数呢?
def get_time(msg=None):
print(msg)
def execute_time(func):
def inner(*args):
t1 = time()
print('begin execute')
result = func(*args)
print('{} execute time is {} s'.format(func.__name__,time()-t1))
return inner
return execute_time
@get_time('this is decorator')
def bubble_sort(nums):
n = len(nums)
for i in range(n):
for j in range(1,n-i):
if nums[j] < nums[j-1]:
nums[j],nums[j-1] = nums[j-1],nums[j]
return nums
nums = []
for i in range(999):
nums.append(random.random())
bubble_sort(nums)
# this is decorator
# begin execute
# bubble_sort execute time is 0.0744624137878418 s
如果需要返回值呢?那就直接在inner返回val
def get_time(msg=None): #最外层参数
print(msg)
def execute_time(func): #函数作为参数
def inner(*args): #函数需要的参数
t1 = time()
print('begin execute')
result = func(*args)
print('{} execute time is {} s'.format(func.__name__,time()-t1))
return result
return inner
return execute_time
@get_time('this is decorator')
def bubble_sort(nums):
n = len(nums)
for i in range(n):
for j in range(1,n-i):
if nums[j] < nums[j-1]:
nums[j],nums[j-1] = nums[j-1],nums[j]
return nums
nums = []
for i in range(9):
nums.append(random.random())
val = bubble_sort(nums)
print(val)
# this is decorator
# begin execute
# bubble_sort execute time is 0.0009984970092773438 s
# [0.32644610054049616, 0.7560608927410807, 0.773970312063855, 0.7937768471304162, 0.8513343757938229, 0.8552826213101954, 0.8685170313819806, 0.9603166404521045, 0.9868069005234218]
五、内置装饰器
1、特性装饰器@property
@property可以将一个实例方法变成同名属性访问。
class Rectangle:
def __init__(self,width,heigh):
self.width = width
self.heigh = heigh
@property
def area(self):
return self.width*self.heigh
rectangle = Rectangle(10,5)
print(rectangle.area)
# 50
2、类方法装饰器@classmethod
@classmethod修饰的方法不需要实例化(可以实例化调用,也可以不实例化调用),直接通过类名调用,不需要self参数,但是需要用cls表示自身类的参数。
class Rectangle:
def __init__(self,width,heigh):
self.width = width
self.heigh = heigh
@property
def area(self):
return self.width*self.heigh
@classmethod
def boundary(cls,x,y):
return 2*(x+y)
@staticmethod
def name():
return 'This is Rectangle'
tangle = Rectangle(10,20)
print(Rectangle.boundary(10,20))
print(tangle.boundary(10,20))
# 60
# 60
3、静态方法装饰器@staticmethod
@staticmethod静态方法类似于普通方法,可以不需要参数或者类的信息,但是这个方法可能与类相关的放在类外面不合适。
class Rectangle:
def __init__(self,width,heigh):
self.width = width
self.heigh = heigh
@property
def area(self):
return self.width*self.heigh
@classmethod
def boundary(cls,x,y):
return 2*(x+y)
@staticmethod
def boundary_nums():
return 4
print(Rectangle.boundary_nums())
# 4
六、类装饰器
装饰器修饰类也是可以的,调用方法和之前类似。
def log(cls):
print('{} is instantiating'.format(cls.__name__))
return cls
@log
class Person:
def __init__(self,name):
self.name = name
person = Person('wangjue')
print(person.name)
参考,对以下博主表示衷心的感谢!
https://www.cnblogs.com/wickedpriest/p/11872402.html
https://www.zhihu.com/question/20021164/answer/18224953
https://www.runoob.com/python/python-func-staticmethod.html
https://zhuanlan.zhihu.com/p/27449649
https://www.zhihu.com/question/325817179/answer/798679602