装饰器

Posted todo000

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了装饰器相关的知识,希望对你有一定的参考价值。

目录:

  函数装饰器

  类装饰器

  

函数装饰器:

  1.装饰器本质是函数,是用来为其他函数增加功能的函数

  2,装饰器需要遵循的原则:

    1.不改变被装饰函数的调用方式

    2.不改变被装饰函数的源代码

  3.实现装饰器需要

    1.闭包

      闭包就是引用了自有变量的函数,这个函数保存了执行的上下文,可以脱离原本的作用域独立存在.

    2.高阶函数

      接收其他函数作为参数,视为高阶函数

    3.嵌套函数

      函数中套函数

装饰器的实现推导:

  使用高阶函数,函数的参数可以接收函数名(函数的内存地址)

import time

def timer(func):                    #装饰器模块
    start_time = time.time()
    func()
    print("用时",time.time() - start_time)

def func():         #被装饰模块
    print(我是测试)
    time.sleep(0.5)
    print("测试结束")

timer(func)         #func函数确实加上了测时功能,但是改变了调用方式
# 我是测试
# 测试结束
# 用时 0.500999927520752

  使用嵌套函数

def timer():
    start_time = time.time()
    def inner():                #在函数体内嵌套一层函数,叫做嵌套函数
        print(start_time)
        time.sleep(0.5)
        print(time.time())
        print("end_time",time.time() - start_time)
    inner()

timer()
# 1569502444.306
# 1569502444.806
# end_time 0.5

  使用闭包

def timer(name):
    def inner(age):
        print("姓名: %s 年龄:%s" %(name,age))       #引用了外部的name变量
    return inner                                    #返回的内部函数的未执行状态

inner = timer("alex")
inner(25)
# 姓名: alex 年龄:25
# 当timer函数被引用时,timer函数已经执行完毕,通常情况下,其拥有的name参数应该被回收
# 但因为其内部的inner函数调用了name变量,使得name变量成为了自由变量
# 因为timer的执行结果返回的是inner的未执行状态,当我们再执行inner时,name的变量得到引用

  我们结合使用高阶函数,嵌套函数,闭包概念

  就可以实现装饰器的功能

  就可以在不改变调用方式的情况下,给函数添加上其他功能

  但这不是装饰器的标准写法

def wrapper(func):
    def inner():
        print("我是装饰器")
        func()
        print("装饰结束")
    return inner

def index():
    print("我是index")

index = wrapper(index)
index()
# 我是装饰器
# 我是index
# 装饰结束

  正确的写法:

def wrapper(func):
    def inner():
        print("我是装饰器")
        func()
        print("装饰结束")
    return inner

@wrapper            #@wrapper 相当于 index = wrapper(index)
def index():
    print("我是index")

index()
# 我是装饰器
# 我是index
# 装饰结束

  我们实现了简单的装饰器

  但这样的被装饰函数没有传参,再给被装饰函数加上传参功能

def wrapper(func):
    def inner(*args,**kwargs):
        print("我是装饰器")
        res = func(*args,**kwargs)
        print("装饰结束")
        return res          #为了获得index()的执行返回值,我们将func()的执行结果返回去,因为func被传入的参数是index,所以得到的就是index()的返回值
    return inner

@wrapper
def index(name):
    print("%s的index" %name)

index("alex")
# 我是装饰器
# alex的index
# 装饰结束

  目前已经可以解决实际应用场景中的绝大部分问题

  在此基础上

  我们还可给装饰加上传参功能

def wrapper(type):
    def outer(func):
        def inner(*args,**kwargs):
            print("我是装饰器")
            print(type)
            res = func(*args,**kwargs)
            print("装饰结束")
            return res
        return inner
    return outer

@wrapper(index)
def index(name):
    print("%s的index" %name)

@wrapper("home")
def home(name):
    print("%s的home" %name)

index(alex)
home("todo")
# 我是装饰器
# index                 装饰器传入的参数
# alex的index            被装饰函数的参数
# 装饰结束
# 我是装饰器
# home                  
# todo的home
# 装饰结束

  装饰器实际应用的两个练习

  1.给函数加上当前时间功能

技术图片
import datetime

def wrapper(func):
    def inner(*args,**kwargs):
        res = func(*args,**kwargs)
        print(datetime.datetime.now())
        return res
    return inner

@wrapper
def index(name):
    print("我是%s的index" %name)
@wrapper
def home(name):
    print("我是%s的home" %name)

index("alex")
home("todo")
# 我是alex的index
# 2019-09-26 22:08:49.297000
# 我是todo的home
# 2019-09-26 22:08:49.297000
View Code

  2.给每个网页加上验证功能,需要多种验证方式

技术图片
import datetime

status = [False]
user = "aaa"
pwd = "123"

def wrapper(type):
    def outer(func):
        def inner(*args,**kwargs):
            if type == local and status[0] == False:
                count = 3
                while count > 0:
                    username = input("输入用户名: ").strip()
                    password = input("输入密码: ").strip()
                    if user == username and pwd == password:
                        res = func(*args,**kwargs)
                        status[0] = True
                        print("验证通过",datetime.datetime.now())
                        return res
                    else:
                        count -= 1
                        print("请重新输入")
                else:
                    print("验证失败")
            elif type == "ldap" and status[0] == False:
                print("验证ldap...")
                res = func(*args,**kwargs)
                return res
            elif status[0] == True:
                print("验证通过")
                res = func(*args,**kwargs)
                return res
        return inner
    return outer

@wrapper("local")
def index(name):
    print("我是%s的index" %name)

@wrapper(local)
def home(name):
    print("我是%s的home" %name)

@wrapper(ldap)
def bbs(name):
    print("我是%s的bbs" %name)

index("alex")
home("todo")
bbs("yi")
View Code

  使用装饰器可以极大的复用函数,但也会产生一个问题,装饰器在给一个函数装饰后,会覆盖其原函数的元信息,当函数被反射调用时,会出现问题

def outer(func):
    def inner(*args,**kwargs):
        print("我是装饰器")
        res = func(*args,**kwargs)
        print("装饰结束")
        return res
    return inner

@outer
def index(name):
    print("我是 %s 的 Index" %name)
    print(index.__name__)

index("alex")
# 我是装饰器
# 我是 alex 的 Index
# inner         #元信息被覆盖,当函数被用到反射调用时,会出现问题
# 装饰结束

  使用functools.wraps方法,解决元信息被覆盖问题

import functools

def outer(func):
    @functools.wraps(func)
    def inner(*args,**kwargs):
        print("我是装饰器")
        res = func(*args,**kwargs)
        print("装饰结束")
        return res
    return inner

@outer
def index(name):
    print("我是 %s 的 Index" %name)
    print(index.__name__)

# 我是装饰器
# 我是 alex 的 Index
# index                     #返回原函数的元信息
# 装饰结束

 

类装饰器

  类方法实现装饰器

import functools

class Wrapper(object):

    def __init__(self,name):
        self.name = name                #装饰器参数

    def __call__(self,func):
        @functools.wraps(func)
        def inner(*args,**kwargs):
            self.info()                     #可以将功能单独实现,这样函数更规范优雅
            res = func(*args,**kwargs)
            self.end("Yes")
            return res
        return inner

    def info(self):
        print("装饰器参数 %s" %self.name)

    def end(self,flag):
        print("装饰结束了吗: %s" %flag)

class Web():
    def __init__(self,name):
        self.name = name

    @Wrapper("wrap")            #传入装饰器参数,实现装饰功能,类似的@property功能也是这样实现的
    def index(self,index_name):
        print("这个是%s网站上的%s的网页" %(self.name,index_name))
        print(self.index.__name__)          #这个函数的元信息

w = Web(alex)
w.index("taobao")

# 装饰器参数 wrap
# 这个是alex网站上的taobao的网页
# index
# 装饰结束了吗: Yes

 

以上是关于装饰器的主要内容,如果未能解决你的问题,请参考以下文章

python 装饰器:装饰器实例类装饰器(装饰函数)

装饰器、装饰器类与类装饰器(三)

Python进阶装饰器(Decorator)

python 装饰器:装饰器实例内置装饰器

python 装饰器:装饰器实例内置装饰器

TS之装饰器