20191127-装饰器

20191126:装饰器

装饰器的定义

装饰器是修改其他函数的功能的函数。@符号是装饰器的语法糖,放在函数开始定义的上方,类似于给函数戴了一个帽子,这个帽子我们称之为装饰器。

当一个函数被装饰器装饰了后,我们调用这个函数的时候并不是真正的执行了这个函数而是将这个函数作为参数传递给装饰器。

概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能

函数装饰器

实现不带参数装饰器

先定义一个装饰函数,一般是一个闭包函数。最简单的装饰器闭包函数如下:

def mydecorater(func):
    def inner(*args,**kwargs):
        print("开始执行初始化操作")
        result = func(*args,**kwargs)
        print("开始执行结束清理操作")
        return result
    return inner

被装饰器装饰的函数写法如下,在fun_need_decorate上方加上@语法糖后接装饰器函数,表示用mydecorater装饰fun_need_decorate函数。

# encoding: utf-8
@mydecorater
def fun_need_decorate(variable_a,variable_b):
    print("func里面的函数执行")
    print("variable_a",variable_a)
    print("variable_b",variable_b)
    return variable_a+variable_b
print(fun_need_decorate(100,99))

开始执行初始化操作

func里面的函数执行

variable_a 100

variable_b 99

开始执行结束清理操作

199

当fun_need_decorate被mydecorater装饰后,fun_need_decorate不再指向fun_need_decorate而是指向对应mydecorater的inner函数的地址,调用fun_need_decorate其实是调用了mydecorater.inner()函数。

此时打印print(fun_need_decorate.__name__)函数的函数名会发现结果输出为inner

如果我们想被装饰的函数输出的函数名仍然是函数本身,即fun_need_decorate.__name__输出结果为fun_need_decorate,需要使用function里面的@wrap装饰器来装饰,用法为修改mydecorater装饰器如下:

from functools import wraps
def mydecorater(func):
    @wraps(func)
    def inner(*args,**kwargs):
        print("开始执行初始化操作")
        result = func(*args,**kwargs)
        print("开始执行结束清理操作")
        return result
    return inner

wrap装饰器的作用

@wraps接受一个函数来进行装饰,并加入了复制函数名称、注释文档、参数列表等等的功能。这可以让我们在装饰器里面访问在装饰之前的函数的属性 

实现带参数的装饰器

实现对不同等级的学生说不同鼓励语言功能的装饰器

def saySomething(level):
    def inner(func):
        def deco(*args,**kwargs):
            if level=="A":
                print("A is find")
                result = func(*args,**kwargs)
                return result+"You are already very good"
           
elif level=="B":
                print("B is find")
                result=func(*args,**kwargs)#真正函数执行
               
return result+"You are good,just more hard work"
           
else:
                print("Other is find")
                result=func(*args,**kwargs)
                return result+"just fighting"
       
return deco
    return inner
@saySomething("A")
def leval_A(variable_a):
    print("variable_a",variable_a)
    return variable_a
@saySomething("B")
def leval_B(variable_a):
    print("variable_a",variable_a)
    return variable_a
print(leval_A.__name__)
print(leval_B.__name__)
print(leval_A("张山"))
print(leval_B("李四"))

类装饰器

实现不带参数的类装饰器

实现类的装饰器必须实现类的__call__方法和__init__方法

__init__接收被装饰函数

__call__实现装饰的逻辑

将上面的不带参数的函数装饰器转换为类装饰器:

# encoding: utf-8
from functools import wraps
class mydecorater:
    def __init__(self,func):
        self.func =func
    def __call__(self, *args, **kwargs):
        print("开始执行初始化操作")
        result = self.func(*args,**kwargs)
        print("开始执行结束清理操作")
        return result

实现带参数的类装饰器

__init__ :不再接收被装饰函数,而是接收传入参数。

__call__ :接收被装饰函数,实现装饰逻辑。

将上面的带参数的函数装饰器转换为类装饰器,此时__init__接收参数,用__call__来接收被装饰的函数,在__call__内部定义一个函数来实现装饰逻辑

# encoding: utf-8
from functools import wraps
class saySomething:
    def __init__(self,level):
        self.level = level
    def __call__(self, func):
        @wraps(func)
        def deco(*args,**kwargs):#实现真正的装饰逻辑
            if self.level == "A":
                print("A is find")
                print("*args", args)
                result = func(*args, **kwargs)
                return result + "You are already very good"
            elif
self.level == "B":
                print("B is find")
                print("*args", args)
                result = func(*args, **kwargs)  # 真正函数执行
               
return result + "You are good,just more hard work"
            else
:
                print("Other is find")
                print("*args", args)
                result = func(*args, **kwargs)
                return result + "just fighting"
        return
deco
@saySomething("A")
def leval_A(variable_a):
    print("variable_a",variable_a)
    return variable_a
@saySomething("B")
def leval_B(variable_a):
    print("variable_a",variable_a)
    return variable_a
print(leval_A.__name__)
print(leval_B.__name__)
print(leval_A("张山"))
print(leval_B("李四"))

如何实现能装饰类的装饰器-用装饰器实现类的单例模式

# encoding: utf-8
from functools import wraps
def singleTol(cls):
    single = {}
    cls_name = cls.__name__
    def deco(*args,**kwargs):
        print("装饰器被调用")
        if cls_name not in single:
            print("if",single)
            instance = cls(*args, **kwargs)
            single[cls_name] = instance
        return single[cls_name]
    return deco
@singleTol
class A:
    def __init__(self,name,age):
        self.name = name
        self.age = age
Ming = A("ming",26)
print(Ming.name)
print(Ming.age)
lily = A("lily",99)
print(lily.name)
print(lily.age)
print(Ming.name)
print(Ming.age)
print(Ming is lily)#True

使用一个字典存储数据,key是类名,value是实例名,如果某一个类有实例,则直接返回该实例,否则的话实例化该类,并且将数据记录入字典,然后返回实例。通过这种方式实现了单例模式

内置装饰器:property

以上,我们介绍的都是自定义的装饰器。

其实Python语言本身也有一些装饰器。比如property这个内建装饰器,我们再熟悉不过了。它通常存在于类中,可以将一个函数定义成一个属性,属性的值就是该函数return的内容,同时,会将这个函数变成另外一个装饰器。

总结

装饰器函数/类的定义:

  1. 不带参数的装饰器是由2层闭包实现,闭包的外层函数接收被装饰的函数,闭包的内层函数进行装饰功能的实现和被装饰函数的执行
  2. 带参数的装饰器是由3层闭包实现的,外层函数接收装饰器参数,中间层函数接收被装饰的函数,内层函数进行装饰功能的实现和装饰函数的执行
  3. 装饰器函数的多层返回的都是其内层函数的地址
  4. 不带参数的类装饰器是由类的__init__方法和__call__方法实现,__init__方法接收被装饰函数,__call__方法进行装饰功能的实现和装饰函数的执行
  5. 带参数的类装饰器是由类的__init__方法和__call__方法实现,此时__init__方法接收的是装饰器的参数,__call__方法接收的是被装饰函数,在__call__方法内部定义真正的装饰函数进行装饰功能的实现和装饰函数的执行

装饰器的作用:

  1. 使代码可读性更高,逼格更高;
  2. 代码结构更加清晰,代码冗余度更低;
  3. 用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景,装饰器是解决这类问题的绝佳设计

原文参考:https://www.jb51.net/article/168276.htm

 

 

猜你喜欢

转载自www.cnblogs.com/hyj691001/p/11939570.html