zoukankan      html  css  js  c++  java
  • 函数的装饰器和类的装饰器

    函数的装饰器

    (1),被装饰的函数没有返回值:

     1 def decorator(func):
     2     def wrapper(arg):
     3         t1 = time.perf_counter()
     4         func(arg)
     5         print(time.perf_counter()-t1)
     6     return wrapper
     7 
     8 @decorator
     9 def func(arg):  #没有返回值
    10     i = 1
    11     while(1):
    12         i+=1
    13         if i>1000:
    14             print(arg)
    15             break
    16 
    17 
    18 if __name__ =="__main__":
    19     import time
    20     func("I am done")
    21 
    22 '''
    23     输出:
    24     I am done
    25     0.00021631981540709086
    26 '''

    (2),被装饰的函数有返回值:

     1 def decorator(func):
     2     def wrapper(arg):
     3         t1 = time.perf_counter()
     4         i = func(arg)
     5         print(time.perf_counter()-t1)
     6         return i
     7     return wrapper
     8 
     9 @decorator
    10 def func(arg):   #有返回值
    11     i = 1
    12     while(1):
    13         i+=1
    14         if i>1000:
    15             print(arg)
    16             break
    17 
    18     return i
    19 
    20 
    21 if __name__ =="__main__":
    22     import time
    23     i = func("I am done")
    24     print(i)
    25 '''
    26     输出:
    27     I am done
    28     9.514658547491372e-05
    29     1001
    30 '''

    内部原理:它利用的就是语法糖@:

    @decorator 在一个函数的上方实际是:

    func = decorator(func)

    证明如下:

     1 def decorator(func):
     2     print("I am decorator")
     3 
     4 @decorator   #---->  func = decorator(func)
     5 def func(arg):
     6     print("I am func")
     7 
     8 
     9 if __name__ =="__main__":
    10     pass
    11 
    12 '''
    13     输出:
    14     I am decorator
    15 '''

    类的装饰器:

    它其实和函数装饰器是一样的,

     1 def decorator(obj):  #装饰器本质上是给对象装饰,obj,所以类对象(类名)也可以
     2     print("-------->",obj)
     3     obj.x = 1 #添加的类属性
     4     obj.y = 2
     5     return obj
     6 
     7 @decorator
     8 class DemoClass:
     9     pass
    10 
    11 
    12 if __name__ =="__main__":
    13     demo = DemoClass()
    14     print(DemoClass.__dict__)
    15     print(demo.__dict__)
    16 '''
    17     输出:
    18     --------> <class '__main__.DemoClass'>
    19     {'__dict__': <attribute '__dict__' of 'DemoClass' objects>, '__module__': '__main__', 'x': 1, '__doc__': None, 'y': 2, '__weakref__': <attribute '__weakref__' of 'DemoClass' objects>}
    20     {}
    21 '''

    补:函数名也是个对象,如下:

     1 def test():
     2     pass
     3 test.x = 1  #理论上是可以这么干的,这说明了一切皆对象
     4 test.y = 2
     5 
     6 if __name__ =="__main__":
     7     print(test.__dict__)
     8 
     9 '''
    10     输出:{'x': 1, 'y': 2}
    11 '''

    而语法糖@的工作就是将它后面的对象作为参数传入,处理完之后再返回该对象。

    继续类的装饰器:

     1 def decorator(obj):
     2     obj.x = 1
     3     return obj 
     4 
     5 @decorator
     6 class Democlass:
     7     pass 
     8 
     9 
    10 if __name__ =="__main__":
    11     pass 
    12    
    13 如果是这样的话,以后再想添加新的类属性就不方便了!

    改进:

     1 def decorator(**kwargs):
     2     def deco(obj):   #真正的装饰,装饰Democlass
     3         for k,v in kwargs.items():
     4             # obj.__dict__[k]=v  #会报错 TypeError: 'mappingproxy' object does not support item assignment
     5             setattr(obj,k,v)  #这种方式可以
     6         return obj
     7     return deco
     8 
     9 @decorator(x=1,y=2,z=3)   #1,先执行decorator(x=1,y=2,z=3) 2,返回结果再执行装饰Democlass
    10 class Democlass:
    11     pass
    12 
    13 
    14 if __name__ =="__main__":
    15     print(Democlass.__dict__)
    16 
    17 '''
    18     输出:
    19     {'__doc__': None, 'y': 2, 'x': 1, 'z': 3, '__module__': '__main__', '__dict__': <attribute '__dict__' of 'Democlass' objects>, '__weakref__': <attribute '__weakref__' of 'Democlass' objects>}
    20 '''

    这就是类的装饰器。

    (装饰器就是个函数)

    类的装饰器的应用

    配合描述符一起使用:(描述符的应用在这:https://www.cnblogs.com/zach0812/p/11312252.html

    class Person:
        name = Check("name",str)
        age = Check("age",int)
        salary = Check("salary",float)  #它们三个都是给类增加属性,所以可以考虑通过类装饰器增加属性
        def __init__(self,name,age,salary):
            self.name = name
            self.age = age
            self.salary = salary
     1 def decorator(**kwargs):
     2     def deco(obj):
     3         for k,v in kwargs.items():
     4             setattr(obj,k,v)
     5         return obj
     6     return deco
     7 
     8 class Check:
     9     def __init__(self,key,type):
    10         self.key = key
    11         self.type= type
    12 
    13     def __set__(self, instance, value):
    14         print("set")
    15         if isinstance(value,self.type):
    16             instance.__dict__[self.key] = value
    17         else:
    18             print("{}输入有误".format(self.key))
    19             # raise TypeError("{}输入有误".format(self.key))
    20     def __get__(self, instance, owner):
    21         print("get")
    22         return instance.__dict__[self.key]
    23 
    24     def __delete__(self, instance):
    25         print("delete")
    26         instance.__dict__.pop(self.key)
    27 
    28 @decorator(name = Check("name",str),age = Check("age",int),salary = Check("salary",float))
    29 class Person:
    30     def __init__(self,name,age,salary):
    31         self.name = name
    32         self.age = age
    33         self.salary = salary
    34 
    35 if __name__ =="__main__":
    36     p1 = Person("tom",18,100.0)
    37     print("============")
    38     p2 = Person(108,"tom","jack")
    39     '''
    40     输出:
    41     set
    42     set
    43     set
    44     ============
    45     set
    46     name输入有误
    47     set
    48     age输入有误
    49     set
    50     salary输入有误
    51     '''

    终结版:

     1 def decorator(**kwargs):
     2     def deco(obj):
     3         for k,v in kwargs.items():
     4             setattr(obj,k,Check(k,v))
     5         return obj
     6     return deco
     7 
     8 class Check:
     9     def __init__(self,key,type):
    10         self.key = key
    11         self.type= type
    12 
    13     def __set__(self, instance, value):
    14         print("set")
    15         if isinstance(value,self.type):
    16             instance.__dict__[self.key] = value
    17         else:
    18             print("{}输入有误".format(self.key))
    19             # raise TypeError("{}输入有误".format(self.key))
    20     def __get__(self, instance, owner):
    21         print("get")
    22         return instance.__dict__[self.key]
    23 
    24     def __delete__(self, instance):
    25         print("delete")
    26         instance.__dict__.pop(self.key)
    27 
    28 @decorator(name = str,age = int,salary = float) #相对上一个,进一步简化代码
    29 class Person:
    30     def __init__(self,name,age,salary):
    31         self.name = name
    32         self.age = age
    33         self.salary = salary
    34 
    35 if __name__ =="__main__":
    36     p1 = Person("tom",18,100.0)
    37     print("============")
    38     p2 = Person(108,"tom","jack")
    39     '''
    40     输出:
    41     set
    42     set
    43     set
    44     ============
    45     set
    46     name输入有误
    47     set
    48     age输入有误
    49     set
    50     salary输入有误
    51     '''

    这就是终结版的对类型检测的程序,

    不过,我们也可以用@property 来进行检测。但是它不好,我们不用,但是这里要说下它!

     1 class DemoClass:
     2     def __init__(self):
     3         pass
     4 
     5     @property
     6     def age(self):
     7         return self._age
     8 
     9     @age.setter
    10     def age(self,val):
    11         if val <0:
    12             self._age = 30
    13         else:
    14             self._age = val
    15 
    16 if __name__=="__main__":
    17     demo = DemoClass()
    18     demo.age = -50
    19     print(demo.age)

    property的使用(2):

     1 class DemoClass:
     2     def __init__(self,name):
     3         self.name = name
     4 
     5     @property
     6     def age(self):
     7         pass
     8 
     9     @age.setter
    10     def age(self,val):
    11         # self.age = val #会构成递归
    12         self.__dict__['age'] =val
    13     @age.getter
    14     def age(self):
    15         # return self.age  #递归
    16         return self.__dict__['age']
    17 
    18     @age.deleter
    19     def age(self):
    20         # del self.age #递归
    21         del self.__dict__['age']
    22 
    23 if __name__ == "__main__":
    24     demo = DemoClass("tom")
    25     demo.age = 18
    26     print(demo.age)
    27     del demo.age
    28     '''
    29     输出:
    30     18
    31     {'age': 18, 'name': 'tom'}
    32     '''
    class DemoClass:
        def __init__(self,name):
            self.name = name
    
        @property  # age = property(age) age 此时为property类 的对象
        def age(self):
            print("2")
            return self.__dict__['age']
        @age.setter
        def age(self,val):
            self.__dict__['age'] =val
        @age.getter
        def age(self):
            print("1")
            return self.__dict__['age']
        @age.deleter
        def age(self):
            del self.__dict__['age']
    
    if __name__ == "__main__":
        demo = DemoClass("tom")
        demo.age = 20
        demo.age
        '''
            输出:1   ,有age.getter()时就不调property()了,没有的话会调它
        
        '''

    它之所以不好用,就是因为它不能复用,每要检测一个属性的取值范围都要新写一个。不像上面描述符写的灵活!

    现在我们利用描述符自己手写个自制的property装饰器.

    之前说的函数的装饰器和类的装饰器都是函数,其实类也可以作为装饰器。

     1 class Myproperty:
     2     def __init__(self,func):
     3         self.func = func
     4 
     5 class DemoClass:
     6     def __init__(self,name):
     7         self.name = name
     8     @Myproperty  # age = Myproperty(age) age 此时为Myproperty类 的对象
     9     def age(self):   #age 在类的属性字典中,是个类属性
    10         print("hello")
    11 
    12 if __name__ == "__main__":
    13     demo = DemoClass("tom")
    14     print(demo.__dict__)
    15     print(DemoClass.__dict__)
    16 
    17     '''
    18     输出:
    19     {'name': 'tom'}
    20     {'__dict__': <attribute '__dict__' of 'DemoClass' objects>, '__doc__': None, '__weakref__': <attribute '__weakref__' of 'DemoClass' objects>, '__init__': <function DemoClass.__init__ at 0x000001A7732C3BF8>, '__module__': '__main__', 'age': <__main__.Myproperty object at 0x000001A7732BFBE0>}
    21     '''

    现在的age是个类属性了,我们可以通过描述符对它进行属性管理(让它成为property那种的不加括号就调用了def age())。

     1 class Myproperty:
     2     def __init__(self,func):
     3         self.func = func
     4 
     5     def __get__(self, instance, owner):  #使Myproperty 成为非数据描述符
     6         return self.func(instance) #instance 是省略的self
     7 
     8 class DemoClass:
     9     def __init__(self,name,width,length):
    10         self.name = name
    11         self.width = width
    12         self.length = length
    13     @Myproperty  # area = Myproperty(area)
    14     def area(self):
    15         return self.width*self.length
    16 
    17 
    18 if __name__ == "__main__":
    19     demo = DemoClass("厕所",15,10)
    20     print(demo.area)  #实际上调的还是def area ()

    @语法糖都是利用描述符的原理来做的,还可以自定制@classmethod 和 classstaticmethod 

  • 相关阅读:
    物联网与边缘计算的融合
    在【自我认知】大学,你可能永远毕不了业
    Spring Security实现短信验证码登录
    线上课程
    【技术人成长】公众号
    大数据是阿猫阿狗都能玩的吗
    机器不能代替你思考
    如何缓解需求沟通中的鸡同鸭讲
    如何成为一个更渊博的技术人
    招聘季,聊聊那些古怪的候选人
  • 原文地址:https://www.cnblogs.com/zach0812/p/11312971.html
Copyright © 2011-2022 走看看