描述符应用与类的装饰器

1.上下文管理协议

with obj as f:
"代码块"
1.with obj ----> 触发obj.__enter__(),拿到返回值
2.返回值赋值f,则f = 返回值
3.执行代码块
3.1.没有异常情况,整个代码块运行完毕后触发__exit__,它的三个参数都不起作用
3.2.有异常情况,从异常出现位置直接触发__exit__
3.2.1如果__exit__的返回值为True,代表吞掉异常,正常执行with函数外的语句
3.2.2如果__exit__的返回值不为True,代表吐出了异常,代码终止
3.2.3exit的运行完毕就代表了整个with语句的执行完毕
__enter__()方法和__exit__()方法的执行
class
Open: def __init__(self,name): self.name = name def __enter__(self): print("执行enter") return self def __exit__(self, exc_type, exc_val, exc_tb): print("执行exit") with Open("a.txt") as f: #with Open("a.txt") as f, 触发__enter__(self) 方法,返回self,并赋值给f print(f) print(f.name) #结束文件时,执行__exit__操作

 return True的打开与关闭会呈现两种状态,对应上面3.2.1和3.2.2

class Open:
    def __init__(self,name):
        self.name = name
    def __enter__(self):
        print("执行enter")
        return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        print("执行exit")
        print(exc_type)
        print(exc_val)
        print(exc_tb)
        return True
with Open("a.txt") as f: #with Open("a.txt") as f, 触发__enter__(self) 方法,返回self,并赋值给f
    print(f)
    print(f.name)
    #结束文件时,执行__exit__操作
    print("=========================>")
    print("=========================>")
    print("=========================>")
    print("=========================>")
    print("=========================>")
    print(asdfsdfsdfsssssdsdddddddddddddddddddd) #本行代码出现异常,跳过with..as..中代码,直接执行with..as..外代码
    print("=========================>")
    print("=========================>")
    print("=========================>")
    print("=========================>")
    print("=========================>")
print("00000000000000000000000000000000000")

2.描述符应用

class str:
    def __get__(self,instance,owner):
        print("str调用")
    def __set__(self,instance,value):
        print("str设置...")
    def __delete__(self,instance):
        print("str删除")
#描述符int
class Int:
    def __get__(self,instance,owner):
        print("Int调用")
    def __set__(self,instance,value):
        print("Int设置...")
    def __delete__(self,instance):
        print("Int删除")
class Typed:
    def __get__(self,instance,owner):
        print('get方法')
        print("instance参数【%s】",instance)
        print("owner参数【%s】"%owner)
    def __set__(self, instance, value):
        print("set方法")
        print("instance参数【%s】" %instance) #instance参数为p1对象地址
        print("value参数【%s】" %value)
    def __delete__(self,instance):
        print("delete方法")
        print("instance参数【%s】"%instance)
class People:
    # name = str()
    # age = Int()
    name = Typed() #name被数据描述符(类属性)代理
    def __init__(self,name,age,salary):
        self.name = name
        self.age = age
        self.salary = salary
p1 = People("alex",18,18.8)
print(p1)
p1.name
p1.name = "egon"
print(p1.__dict__)

描述符字符检测(一个变量):

class Typed:
    def __init__(self,key):
        self.key = key
        # self.expected_type = expected_type
    def __get__(self,instance,owner):
        print('get方法')
        # print("instance参数【%s】",instance)
        # print("owner参数【%s】"%owner)
        return instance.__dict__[self.key]
    def __set__(self, instance, value): #描述符 123-->name-->value--底层字典instance.__dict__[self.key]
        print(value)
        print("set方法")
        if not isinstance(value,str):#比较字符串类型
            print("传入类型有误")
            return
            # raise TypeError("传入类型有误")
        instance.__dict__[self.key] = value
    def __delete__(self,instance):
        print("delete方法")
        # print("instance参数【%s】"%instance)
        instance.__dict__.pop(self.key)
class People:
    # name = str()
    # age = Int()
    name = Typed("name") #name被数据描述符(类属性)代理,所以不直接传入instance底层字典,age和salary未被数据描述符占用,age,salary作为实例属性传入底层字典
    def __init__(self,name,age,salary):
        self.name = name
        self.age = age
        self.salary = salary
p1 = People("jack",25,25.5)
print(p1.__dict__)
print(p1.name)
del p1.name
print(p1.__dict__)
p2 = People(123,25,25.5) #name为非字符串时,认为传入有误,不向字典中设置值
print(p2.__dict__)

 描述符字符检测(三个变量):

class Typed:
    def __init__(self,key,expected_type):
        self.key = key
        self.expected_type = expected_type
    def __get__(self,instance,owner):
        print('get方法')
        # print("instance参数【%s】",instance)
        # print("owner参数【%s】"%owner)
        return instance.__dict__[self.key]
    def __set__(self, instance, value): #描述符 123-->name-->value--底层字典instance.__dict__[self.key]
        print("set方法")
        if not isinstance(value,self.expected_type):#比较字符串类型
            # print("传入类型有误")
            # return
            raise TypeError("%s 传入类型不是 %s" %(self.key,self.expected_type))
        instance.__dict__[self.key] = value
    def __delete__(self,instance):
        print("delete方法")
        # print("instance参数【%s】"%instance)
        instance.__dict__.pop(self.key)

class People:
    name = Typed("name",str) #name被数据描述符(类属性)代理,所以不直接传入instance底层字典,age和salary未被数据描述符占用,age,salary作为实例属性传入底层字典
    age = Typed("age",int)
    salary = Typed("salary",float)
    def __init__(self,name,age,salary):
        self.name = name
        self.age = age
        self.salary = salary

p1 = People("yuyukun",233,45.5)
print(p1.__dict__)

3.类装饰器引入帖

https://www.zhihu.com/question/26930016

4.类装饰器基本原理

def deco(func):
    print("+++++++++++++++++")
    return func
@deco  #test=deco(test)
def test():
    print("test函数运行")
test()
def deco(obj):
    print("+++++++++++++++++++++++++++++++++++++++",obj)
    obj.x = 1
    obj.y = 2
    obj.z = 3
    return obj
@deco #Foo=deco(Foo)
class Foo:
    print("test")
f1 = Foo()
print(f1)
print(Foo.__dict__)

类装饰器增强功能,向底层字典中固定设置值

def Typed(**kwargs):
    def deco(obj):
        for key,val in kwargs.items():
            setattr(obj,key,val)
        return obj
    return deco
@Typed(x=1,y=2,z=3) #1.Typed(x=1,y=2,z=3)--->deco 2.@deco--->Foo=deco(Foo)
class Foo:
    pass
print(Foo.__dict__) #向底层Foo字典传入x:1,y:2,z:3
@Typed(name="alex") #1.Typed(name="alex")--->deco 2.@deco--->Bar=deco(Bar)
class Bar:
    print(12345)
Bar()
print(Bar.__dict__)

类装饰器的应用

类装饰器在限定变量类型中的应用:代码分单个部分 1定义描述符类,设置描述符 2添加装饰器和设置两层函数向字典中加入key,value值 3添加装饰器和定义数据属性类

class Typed:
    def __init__(self,key,expected_type):
        self.key = key
        self.expected_type = expected_type
    def __get__(self,instance,owner):
        print('get方法')
        return instance.__dict__[self.key]
    def __set__(self, instance, value): #描述符 123-->name-->value--底层字典instance.__dict__[self.key]
        print("set方法")
        if not isinstance(value,self.expected_type):#比较字符串类型
            raise TypeError("%s 传入类型不是 %s" %(self.key,self.expected_type))
        instance.__dict__[self.key] = value
    def __delete__(self,instance):
        print("delete方法")
        instance.__dict__.pop(self.key)

def deco(**kwargs):  #kwargs={"name":str,"age":int}
    def wrapper(obj): #obj=People
        for key,val in kwargs.items(): #(("name",str),("age",int))
            # print("==============>",key,val)
            setattr(obj,key,Typed(key,val))
            # 上面行代码等同于setattr(People,"name",Typed("name",str))  #装饰器作用,替换这条代码 People.name=Typed("name",str)
        return obj
    return wrapper

@deco(name=str,age=int) #@wrapper ===> People=wrapper(People)
class People:
    # name = "alex"
    # name = Typed("name",str) #name被数据描述符(类属性)代理,所以不直接传入instance底层字典,age和salary未被数据描述符占用,age,salary作为实例属性传入底层字典
    def __init__(self,name,age,salary,gender,height):
        self.name = name
        self.age = age
        self.salary = salary

p1 = People("alex",233,45.5,"x","y")
print(People.__dict__)

描述符总结:可以实现大部分python类特性中的底层魔法,包括@classmethod,@staticmethod,@property,甚至是__slots__属性

描述符是众多高级库和框架的重要工具之一,描述符通常是使用到装饰器或者元类的大型框架的一个组件

5.利用描述符自定制property

class Lazyproperty:
    def __init__(self,func): #数据描述符
        print("=======>",func)
        self.func = func
    def __get__(self, instance, owner):
        print("get")
        print(instance) #instance = <__main__.Room object at 0x000000000290C2E8>
        print(owner) #Room类 owner = <class '__main__.Room'>
        res = self.func(instance)
        return res
class Room:
    x = Lazyproperty("x")
    def __init__(self, name, width, length):
        self.name = name
        self.width = width
        self.length = length
    #@property #area = property(area)
    @Lazyproperty #执行lazyproperty  #area=Lazyproperty(area)  @称为语法糖  Lazyproperty(area)返回值是实例化得到一个对象
    def area(self):
        return self.width * self.length

# print(Room.__dict__)
r1 = Room("厕所",1,1)  #实例化的结果是Lazyproperty的返回值
# print(r1.__dict__)
print(r1.area) #实例属性没有area这个对象,到类中找,类中area被代理,于是触发__get__()方法

描述符,类函数,实例属性调用逻辑,描述符__get__()方法得两参数,instance,owner

描述符用来代理别人属性,别人指类,代理两个方法,1类自己 2类衍生出来的实例
实例属性一旦调用,触发描述符中__get__()方法
实例调用传入,返回值为属性自己
类调用传入值到实例属性,返回值为None
向实例属性字典中添加值的操作,实现延迟功能
class Lazyproperty:
    def __init__(self,func):
        self.func = func
    def __get__(self, instance, owner):#非数据描述符
        if instance is None:
            return self
        res = self.func(instance)
        setattr(instance,self.func.__name__,res)#添加值到实例属性中
        return res
    # def __set__(self, instance, value): #添加__set__函数做成数据描述符,调用实例属性时,会经过此数据描述符,而不走__get__()非数据描述符,同时也不向r1.__dict__中添加area值
    #     print("set")
class Room:
    def __init__(self,name,width,length):
        self.name = name
        self.width = width
        self.length = length
    @Lazyproperty
    def area(self):
        return self.width * self.length
    @property
    def area1(self):
        return self.width * self.length
r1 = Room("厕所",1,1)
print(r1.area)
print(r1.__dict__)
print(r1.area)
#代码作用,向实例属性中添加area,再次调用r1.area时,可直接从实例属性中调取,而不是从非数据描述符中调取,实现延迟计算
print(r1.area)

6.ClassMethod用法,在类中添加文件

class ClassMethod:
    def __init__(self,func):
        self.func = func
    def __get__(self, instance, owner):
        def feedback(*args,**kwargs):
            print("在这里可以加功能啊")
            return self.func(owner,*args,**kwargs)
        return feedback
class People:
    name = "linhaifeng"
    @ClassMethod #say_hi=ClassMethod(say_hi)
    def say_hi(cls,msg,x):
        print("你好啊,帅哥 %s %s %s" %(cls.name,msg,x))
People.say_hi("你是哪偷心的贼",10)

7.人为制作property,启动get_AAA, set_AAA, del_AAA

class Foo:
    # @AAA.setter
    def get_AAA(self):
        print("get的时候运行我啊")
    # @AAA.setter
    def set_AAA(self,val):
        print("set的那时候运行我啊")
    def del_AAA(self): #(del的那时候运行我)
    # @AAA.deleter:
        print("del的时候运行我啊")
    AAA=property(get_AAA,set_AAA,del_AAA)
f1=Foo() #静态属性只有一个值时,不可以设值
f1.AAA
f1.AAA="aaa"
del f1.AAA

property用法

class Goods:
    def __init__(self):
        self.original_price = 100
        self.discount = 0.8

    @property
    def price(self):
        new_price = self.original_price * self.discount
        return new_price

    @price.setter
    def price(self,value):
        self.original_price = value

    @price.deleter
    def price(self):
        del self.original_price

obj = Goods()
print(obj.price)
obj.price = 200
print(obj.price)
del obj.price

元类

class Foo:
    def __init__(self):
        pass
print(Foo) #类名
print(Foo.__dict__)

def __init__(self,name,age):
    self.name = name
    self.age = age
def test(self):
    print("========>")
FFo = type("FFo",(object,),{"x":1,"__init__":__init__,"test":test})
print(FFo)
print(FFo.__dict__)
f1 = FFo("alex",18)
print(f1.name)
f1.test()
自定制元类,向实例属性字典中添加内容
思路:1调用实例属性;2进入Foo类,触发MyType类,并返回Foo对象;3执行Foo(XXX)类对象,触发__call__内置属性函数,返回Foo类名
class MyType(type):
    def __init__(self,a,b,c):
        pass
    def __call__(self, *args, **kwargs):
        print(self)
        obj = object.__new__(self) #object.__new__(Foo)-->f1
        self.__init__(obj,*args,**kwargs) #Foo.__init__(f1,*arg,**kwargs)
        return obj

class Foo(metaclass=MyType): #Foo=MyType(Foo,'Foo',(),{})---》__init__
    def __init__(self,name):
        self.name = name

f1 = Foo("alex") #1执行Foo() 2执行__call__
print(f1)
print(f1.__dict__)

 
原文地址:https://www.cnblogs.com/yuyukun/p/10684609.html