Python开发之路 - 描述符的应用

1.描述符的应用

用描述符实行类型检测

class Dtype:
    def __init__(self,key,expect_type):
        self.key = key 
        self.expect_type = expect_type

    def __get__(self, instance, owner):
        return instance.__dict__[self.key]

    def __set__(self, instance, value):
        if not isinstance(value,self.expect_type):
            raise TypeError('%s传入的类型有误,应该为%s'%(self.key,self.expect_type))
        instance.__dict__[self.key] = value

    def __delete__(self, instance):
        instance.__dict__.pop[self.key]
class People:
    name = Dtype('name',str)
    age = Dtype('age',int)
    def __init__(self,name,age):
        self.name = name
        self.age = age
p1 = People('chris','18')
print(p1.__dict__)

 2.类的装饰器

def daju(**kwargs):
    def deco(obj):
        for k,v in kwargs.items():
            setattr(obj,k,v)
        return obj
    return deco
@daju(x=1,y=2)  #先运行daju(x=1,y=2) 返回一个deco 相当于@deco  Foo=deco(Foo) 
class Foo:
    pass
print(Foo.__dict__)

此时实现了 对类的修饰 加入了x,y的属性

加入对类型的限制:

class Typed:
    def __init__(self,key,expect_type):
        self.key = key
        self.expect_type = expect_type
    def __set__(self, instance, value):
        if not isinstance(value,self.expect_type):
            raise TypeError('%s输入的类型有误,应为%s类型' %(self.key,self.expect_type))
        instance.__dict__[self.key] = value


def deco(**kwargs):  #kwargs 接收到 {'name' = str, 'age' = int, 'salary' = float}
    def wrapper(obj):
        for k,v in kwargs.items():
            print(k,v)
            setattr(obj,k,Typed(k,v))  #设置值时,将K给描述符代理 实现类型限制 既name = Typed('name',str)
            #setattr(Foo,'name',Typed('name',str)) 上面相当于执行这个  Foo.name = Typed('name',str)
        return obj
    return wrapper

@deco(name = str, age = int, salary = float) #装饰器代替了三个重复的代码 实现给Foo类设定属性 只不过这个属性是描述符 对类的类型实现限制
class Foo:
    # name = Typed('name',str)
    # age = Typed('age',int)
    # salary = Typed('salary',str)
    def __init__(self,name,age,salary):
        self.name = name
        self.age = age
        self.salary = salary
p1 = Foo('chris',19,25.7)
print(Foo.__dict__)
print(p1.__dict__)

3.利用描述符自定制property

class Lazyproperty:
    def __init__(self,func):
        self.func = func
        # print('22222')
    def __get__(self, instance, owner):  #实例调用时instance是实例自己 owner 为其所在的类 类调用时instance 为 None owner为类自己
        if instance is None:
            return self
        val = self.func(instance)
        return val
class Room:
    def __init__(self,name,length,width):
        self.name = name
        self.length = length
        self.width = width
    @Lazyproperty  # area = Lazyproperpy(area) 也是在增加描述符
    def area(self):
        return self.length * self.width

r1 = Room('厕所',18,23)
print(Room.area)
print(r1.area)
print(r1.area)
print(r1.area)

 

 !!!此时实例调用area方法时会触发get方法,get方法中会自动将area算出来的值保存在实例的字典中,又因为Lazyproperty为非数据描述符,优先级低于实例属性,所以下次调用area方法时,会优先从实例属性字典中查找,所以不会触发get方法

4.自定制property功能实现延迟计算功能:定义set方法使描述符成为数据描述符,因此每次调用时都会从数据描述符中调用get方法

import  time
class Lazyproperty:
    def __init__(self,func):
        self.func = func
        # print('22222')
    def __get__(self, instance, owner):  #实例调用时instance是实例自己 owner 为其所在的类 类调用时instance 为 None owner为类自己
        time.sleep(2)
        print('get方法')
        if instance is None:
            return self
        val = self.func(instance)
        setattr(instance,self.func.__name__,val) #将area算出来的结果存放到实例自己的属性名为area的字典当中
        return val
    def __set__(self, instance, value):
        pass
class Room:
    def __init__(self,name,length,width):
        self.name = name
        self.length = length
        self.width = width
    @Lazyproperty  # area = Lazyproperpy(area) 也是在增加描述符
    def area(self):
        return self.length * self.width

r1 = Room('厕所',18,23)
print(r1.area)
print(r1.area)
print(r1.area)

 5.利用描述符自定制一个classmethod

class ClassMethod:
    def __init__(self,func):
        self.func=func

    def __get__(self, instance, owner): #类来调用,instance为None,owner为类本身,实例来调用,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):
        print('你好啊,帅哥 %s %s' %(cls.name,msg))

People.say_hi('你是那偷心的贼')

p1=People()
p1.say_hi('你是那偷心的贼')

 

6.property补充 为静态属性设置值时用的。setter方法 删除静态属性值时 用.deleter方法

class Foo:
    @property
    def AAA(self):
        print('get的时候运行我啊')

    @AAA.setter
    def AAA(self,value):
        print('set的时候运行我啊')

    @AAA.deleter
    def AAA(self):
        print('delete的时候运行我啊')

#只有在属性AAA定义property后才能定义AAA.setter,AAA.deleter
f1=Foo()
f1.AAA
f1.AAA='aaa'
del f1.AAA
或者
lass Foo:
    def get_AAA(self):
        print('get的时候运行我啊')

    def set_AAA(self,value):
        print('set的时候运行我啊')

    def delete_AAA(self):
        print('delete的时候运行我啊')
    AAA=property(get_AAA,set_AAA,delete_AAA) #内置property三个参数与get,set,delete一一对应

f1=Foo()
f1.AAA
f1.AAA='aaa'
del f1.AAA
复制代码

 

应用场景如下:

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()
obj.price         # 获取商品价格
obj.price = 200   # 修改商品原价
print(obj.price)
del obj.price     # 删除商品原价
View Code

 

7.元类

元类是类的类,是类的模板,元类是用来控制如何创建类的,元类的实例是类,正如类的实例是对象

type是pyhton的一个内建元类,用来直接控制生成类

FFo = type('Foo',(object,),{'x':1}) # 用type创建类 第一个参数为类名,第二个参数为继承的类,新式类默认加个object,第三个参数是属性

模拟元类帮你生成对象的过程:可以自己控制实例化的过程 定制自己的逻辑

class Mytype(type):
    def __init__(self,a,b,c):
        print('这是我的元类')

    def __call__(self, *args, **kwargs):
        obj = object.__new__(self) ##产生一个Foo的对象 self就是Foo
        self.__init__(obj,*args,**kwargs) #为Foo生成的对象的字典里加属性
        return obj
class Foo(metaclass=Mytype):  #相当于MYtype('Foo',(object),{}) 传过去四个参数
    def __init__(self,name):
        self.name = name
f1 = Foo('chris')  #相当于在执行Mytype的call方法
posted @ 2020-03-07 22:39  五号世界  阅读(201)  评论(0编辑  收藏  举报