搞懂Python装饰器

一、故事背景

石原里美在学习排序算法时看到了这个博客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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值