装饰器

作者: 凉风有信2020 | 来源:发表于2020-08-25 16:51 被阅读0次

装饰器是python中较为高级的用法,它的使用也是比较不容易理解的。

在最近的学习中,找到一个讲解的很清楚的视频,解决了我多年的疑惑,放在文后。

之前面试的时候也问到了这个知识点,我只能连蒙带猜的胡说了一通,现在学完再回过头一看,我如果是面试官也不会自己过吧,哈哈哈。

那么现在我们就来一起学习装饰器

import time

def is_prime(num):
    if num < 2: return False
    elif num == 2: return True
    else:
        for i in range(2,num):
            if num % 2 == 0: return False
        return True

def prime_nums():
    t1 = time.time()
    for i in range(2,10000):
        if is_prime(i):
            print(i)
    t2 = time.time()
    print(t1,t2,t2-t1)

prime_nums()

首先是一段简单的代码,用来统计0-9999中的质数,没有用到装饰器,通过time模块来统计prime_nums函数运行的时间,运行一遍是9.72s。

函数本身没什么问题,但是我们的prime_nums函数是一个用来统计质数的函数,统计时间的功能在这里是突兀的,我们想要将这个功能移出函数,放在另一个函数中,这就有了第二个程序

import time

def display_time(func):
    t1 = time.time()
    func()
    t2 = time.time()
    print(t1,t2,t2-t1)

def is_prime(num):
    if num < 2: return False
    elif num == 2: return True
    else:
        for i in range(2,num):
            if num % 2 == 0: return False
        return True

def prime_nums():
    for i in range(2,10000):
        if is_prime(i):
            print(i)

display_time(prime_nums)

这个程序在原程序的基础上将计时功能抽离了出来,作为display_time函数。这个函数的参数是函数名,python是允许将函数名作为参数进行传递,而不会在传递过程中执行这个函数的,直到出现func()这样的时候才会真正执行它。将功能抽取出来后,用时2.22s,可以发现,节省了许多运行时间。

但是,有个问题,我们最后执行的是display_time(prime_nums),这里有一个主从的关系,看起来似乎主体是显示时间,但实际上显示时间只是一个附加的功能,主要的功能是找到质数。这样写不利于我们阅读程序。

同时如果保持现状,在开发完成后,我们提供给测试者和使用者的将会是display_time()这个功能,这将会带来一些歧义。而我们想提供其实是prime_nums()这样一个功能。因此,得益于python的语法糖,我们可以继续改进:

import time

def display_time(func):
    def wrapper():
        t1 = time.time()
        func()
        t2 = time.time()
        print(t1,t2,t2-t1)
    return wrapper

def is_prime(num):
    if num < 2: return False
    elif num == 2: return True
    else:
        for i in range(2,num):
            if num % 2 == 0: return False
        return True

@display_time
def prime_nums():
    for i in range(2,10000):
        if is_prime(i):
            print(i)

prime_nums()

现在我们的display_time就是一个装饰器了,他会返回一个wrapper的函数名,而这个函数实际上是对func()做了增强,增加了计时的功能。

此时,执行用@display_time装饰了的prime_nums时,实际上是执行了display_time(prime_nums)中的wrapper(),并在wrapper中执行了prime_nums()

所谓面向切面编程,就是将一个复杂的函数分为一个个小的功能,在需要修改时只需要修改一小部分,而不需要对整个的大函数进行修改。

我们的第一个装饰器跑通了,但现还要对他做一些改进:

  • 一个是如果要传参,而不是固定10000的时候
  • 一个是如果有返回值的时候
    函数就需要做一些改进,首先是处理返回值:
import time

def display_time(func):
    def wrapper():
        t1 = time.time()
        result = func()
        t2 = time.time()
        print(t1,t2,t2-t1)
        return result
    return wrapper

def is_prime(num):
    if num < 2: return False
    elif num == 2: return True
    else:
        for i in range(2,num):
            if num % 2 == 0: return False
        return True

@display_time
def prime_nums():
    count = 0
    for i in range(2,10000):
        if is_prime(i): count += 1
    return count

count = prime_nums()
print(count)

接上文所述,因为实际执行的是wrapper(),所以wrapper()必须有一个返回值,我们将func()的结果用result保存起来,再返回就可以了。

接下来是处理参数:

import time

def display_time(func):
    def wrapper(*args,**kwargs):
        t1 = time.time()
        result = func(*args,**kwargs)
        t2 = time.time()
        print(t1,t2,t2-t1)
        return result
    return wrapper

def is_prime(num):
    if num < 2: return False
    elif num == 2: return True
    else:
        for i in range(2,num):
            if num % 2 == 0: return False
        return True

@display_time
def prime_nums(max_num):
    count = 0
    for i in range(2,max_num):
        if is_prime(i): count += 1
    return count

count = prime_nums(10000)
print(count)

解决办法就是在wrapper()中将写上参数。本题中其实也可以写wrapper(max_num)和result = func(max_num),但是在处理有很多参数的函数时,使用wrapper(*args,**kwargs)的写法将所有的参数传递过去是非常方便的。

同时,在这里我们可以更加容易的看出来,
@display_time
def prime_nums(max_num):
这样的写法,其实等价于display_time(prime_nums)(max_num)。display_time(prime_nums) == wrapper,最后执行的是wrapper(max_num),只不过wrapper是在display_time中执行,能够调用func = prime_nums而已。

好了,装饰器的基础知识就学习完了,想要深入了解,重要的还是多看,多练,多思考。

大佬的视频在这里,讲得非常不错:https://www.bilibili.com/video/BV11s411V7Dt?from=search&seid=12540511450060276795

相关文章

  • 装饰器

    """@装饰器- 普通装饰器- 带参数的装饰器- 通用装饰器- 装饰器装饰类- 内置装饰器- 缓存装饰器- 类实现...

  • typescript 五种装饰器

    装饰器类型 装饰器的类型有:类装饰器、访问器装饰器、属性装饰器、方法装饰器、参数装饰器,但是没有函数装饰器(fun...

  • python——装饰器详解

    一、装饰器概念 1、装饰器 装饰器:一种返回值也是一个函数的函数,即装饰器。 2、装饰器目的 装饰器的目的:装饰器...

  • Python装饰器

    Python装饰器 一、函数装饰器 1.无参装饰器 示例:日志记录装饰器 2.带参装饰器 示例: 二、类装饰器 示例:

  • Python中的装饰器

    Python中的装饰器 不带参数的装饰器 带参数的装饰器 类装饰器 functools.wraps 使用装饰器极大...

  • 装饰器

    装饰器 decorator类装饰器 带参数的装饰器 举例(装饰器函数;装饰器类;有参与无参) https://fo...

  • TypeScript装饰器

    前言 装饰器分类 类装饰器 属性装饰器 方法装饰器 参数装饰器需要在tsconfig.json中启用experim...

  • python之装饰器模版

    装饰器的作用:装饰器即可以装饰函数也可以装饰类。装饰器的原理:函数也是对象 1.定义装饰器 2.使用装饰器假设de...

  • 装饰器实验

    装饰器实验 说明 ts内包含了四个装饰器,类装饰器、属性装饰器、函数装饰器、参数装饰器,本文中测试一下其的使用。 ...

  • python3基础---详解装饰器

    1、装饰器原理 2、装饰器语法 3、装饰器执行的时间 装饰器在Python解释器执行的时候,就会进行自动装饰,并不...

网友评论

      本文标题:装饰器

      本文链接:https://www.haomeiwen.com/subject/jerzjktx.html