面向对象_描述符

  描述符就是一个新式类,在这个新式类中,至少实现了__get__(),__set__(),__delete__()中的一个,这也被称为描述符协议

__get__():调用一个属性时触发
__set__():为一个属性赋值时触发
__delete__():使用delete删除属性时触发

  描述符的参数

class Str():
    def __get__(self, instance, owner):
        print('from __get__...')
        print('self',self)        #<__main__.Str object at 0x02D80210>类Str的对象即p1的name属性
        print('instance',instance)#<__main__.People object at 0x02D801D0>类People的对象即p1
        print('owner',owner)      #<class '__main__.People'>类People

    def __set__(self, instance, value):
        print('from __set__...')
        print('self',self)        #<__main__.Str object at 0x02D80210>
        print('instance',instance)#<__main__.People object at 0x02D801D0>
        print('value',value)      # lary  p1.name属性的value

    def __delete__(self, instance):
        print('from __delete__...')
        print('self',self)        #<__main__.Str object at 0x02D80210>
        print('instance',instance)#<__main__.People object at 0x02D801D0>

class People():
    name = Str()
    def __init__(self,name,age):
        self.name = name
        self.age = age

p1 = People('lary',18)
p1.name
del p1.name

触发

  描述符的作用是用来代理另外一个类的属性的(必须把描述符定义成这个类的类属性,不能定义到构造函数中)

#描述符类产生的实例进行属性操作并不会触发三个方法的执行
class Foo():
    def __get__(self, instance, owner):
        print('use get')

    def __set__(self, instance, value):
        print('use set')

    def __delete__(self, instance):
        print('use delete')

f1 = Foo()
f1.name='lary'
#描述符代理另外一个类的类属性时才会触发

class Str:
    def __get__(self, instance, owner):
        print('Str调用')
    def __set__(self, instance, value):
        print('Str设置...')
    def __delete__(self, instance):
        print('Str删除...')

class People:
    name=Str()
    def __init__(self,name,age): #name被Str类代理
        self.name=name
        self.age=age

p1 = People('lary',18)
p1.name

分类

  描述符分为数据描述符和非数据描述符

#类别一 数据描述符:至少实现了__get__()和__set__()
class Str:
    def __get__(self, instance, owner):
        print('Str调用')
    def __set__(self, instance, value):
        print('Str设置...')
  
#类别二 非数据描述符:没有实现__set__()
class Str:
    def __get__(self, instance, owner):
        print('Str调用')
   

优先级

  描述符本身应该被定义成新式类,被代理的类也应该是新式类

  必须把描述符定义成这个类的类属性,不能定义到构造函数中

  描述符与类的属性有优先级顺序,必须遵循该优先级

描述符与类的优先级由高到低
类属性
数据描述符
实例属性
非数据描述符
找不到的属性触发__getattr__()
class Str:
    def __get__(self, instance, owner):
        print('Str调用')
    def __set__(self, instance, value):
        print('Str设置...')
    def __delete__(self, instance):
        print('Str删除...')

class People:
    name=Str()
    def __init__(self,name,age): #name被Str类代理,age被Int类代理,
        self.name=name
        self.age=age

print(People.name)
People.name = 'lary'
print('before',People.__dict__)
print('---')
del People.name
print('after',People.__dict__)
类属性大于数据描述符
class Str:
    def __get__(self, instance, owner):
        print('Str调用')
    def __set__(self, instance, value):
        print('Str设置...')
    def __delete__(self, instance):
        print('Str删除...')

class Int:
    def __get__(self, instance, owner):
        print('Int调用')
    def __set__(self, instance, value):
        print('Int设置...')
    def __delete__(self, instance):
        print('Int删除...')

class People:
    name=Str()
    age=Int()
    def __init__(self,name,age): #name被Str类代理,age被Int类代理,
        self.name=name
        self.age=age

p1=People('lily',18)

p1.name
p1.age
数据描述符大于实例属性
class Foo:
    def func(self):
        print('我胡汉三又回来了')

f1 = Foo()
f1.func()                                 #调用类的方法,也可以说是调用非数据描述符,函数是一个非数据描述符对象
print(dir(Foo.func))
print(hasattr(Foo.func,'__delete__'))
print(hasattr(Foo.func,'__set__'))
print(hasattr(Foo.func,'__get__'))


f1.func = '这是实例属性啊'
print(f1.func)

del f1.func

class Foo:                                #至少实现了__set__和__get__方法的为数据描述符
    def __set__(self, instance, value):
        print('set')
    def __get__(self, instance, owner):
        print('get')
class Room:
    name=Foo()
    def __init__(self,name,width,length):
        self.name=name
        self.width=width
        self.length=length

r1 = Room('厕所',1,1)
r1.name
r1.name='厨房'


class Foo:
    def __get__(self, instance, owner):   #只实现了__get__方法的为非数据描述符
        print('get')
class Room:
    name=Foo()
    def __init__(self,name,width,length):
        self.name=name
        self.width=width
        self.length=length

r1 = Room('厕所',1,1)
print(r1.name)
r1.name = '厨房'
print(r1.name)
实例属性大于非数据描述符
class Foo(object):

    # def __getattribute__(self, item):
    #     print('能不能找到都会来找我', item)

    def func(self):
        print('我胡汉三又回来了')


f1=Foo()
# f1.xxxxx
# f1.func
print(f1.__dict__)
f1.func()


class animal(object):

    def __getattribute__(self, item):
       return object.__getattribute__(self,item)()

    def eat(self):
        print('eating...')

#print(animal.__dict__)
cat = animal()
#print(cat.__dict__)
cat.eat
#当获取属性时,直接return object.__getattribute__(self,*args,**kwargs)
#如果需要获取某个方法的返回值时,则需要在函数后面加上一个()即可,如果不加的话,返回的是函数引用地址
#在__getattribute__方法里面,不能用self.xxx这种方式调用。因为调用类的属性每次都会强制调用__getattribute__,所以会导致递归调用
非数据描述符大于找不到

使用

class Typed:
    def __init__(self,name,expected_type):
        self.name=name
        self.expected_type=expected_type
    def __get__(self, instance, owner):
        print('get--->',instance,owner)
        if instance is None:
            return self
        return instance.__dict__[self.name]

    def __set__(self, instance, value):
        print('set--->',instance,value)
        if not isinstance(value,self.expected_type):
            raise TypeError('Expected %s' %str(self.expected_type))
        instance.__dict__[self.name]=value
    def __delete__(self, instance):
        print('delete--->',instance)
        instance.__dict__.pop(self.name)


class People:
    name=Typed('name',str)
    age=Typed('name',int)
    salary=Typed('name',float)
    def __init__(self,name,age,salary):
        self.name=name
        self.age=age
        self.salary=salary

p1=People(123,18,3333.3)
p1=People('egon','18',3333.3)
p1=People('egon',18,3333)
p1=People('egon',18,3333.3)
数据类型限制

  如果我们的类有很多属性,可以使用类的装饰器来使用描述符

def decorate(cls):
    print('类的装饰器开始运行啦')
    return cls

@decorate
class People:
    def __init__(self,name,age,salary):
        self.name=name
        self.age=age
        self.salary=salary
p1 = People('lary',18,4321.3)
print(p1.name)
类装饰器:无参
def typeassert(**kwargs):
    def decorate(cls):
        print('类的装饰器开始运行啦',kwargs)
        return  cls
    return decorate
@typeassert(name=str,age=int,salary=float)
class People:
    def __init__(self,name,age,salary):
        self.name=name
        self.age=age
        self.salary=salary
p1 = People('lary',18,3434.1)
print(p1.name)
类装饰器:有参
#描述符与装饰器的应用
class Typed:
    def __init__(self,name,expected_type):
        self.name=name
        self.expected_type=expected_type
    def __get__(self, instance, owner):
        print('get--->',instance,owner)
        if instance is None:
            return self
        return instance.__dict__[self.name]

    def __set__(self, instance, value):
        print('set--->',instance,value)
        if not isinstance(value,self.expected_type):
            raise TypeError('Expected %s' %str(self.expected_type))
        instance.__dict__[self.name]=value
    def __delete__(self, instance):
        print('delete--->',instance)
        instance.__dict__.pop(self.name)

def typeassert(**kwargs):
    def decorate(cls):
        print('类的装饰器开始运行啦',kwargs)
        for name,expected_type in kwargs.items():
            setattr(cls,name,Typed(name,expected_type))
        return cls
    return decorate

@typeassert(name=str,age=int,salary=float)
class People:
    def __init__(self,name,age,salary):
        self.name = name
        self.age = age
        self.salary = salary

print(People.__dict__)
p1 = People('lary',18,2343.2)

  自定义属性

# property
class Animal():
    def __init__(self,name):
        self.name = name

    @property
    def eat(self):
        return self.name


animal1 = Animal('cat')
print(animal1.eat)


#自定义property
#调用传过来的func函数,将对象(instance)作为参数(即类中函数需要的参数self)
class Lazyproperty():
    def __init__(self,func):
        self.func = func

    def __get__(self, instance, owner):
        print('这是我们自己定制的静态属性,r1.area实际是要执行r1.area()')
        if instance is None:
            return self
        return self.func(instance)

class Room():
    def __init__(self,name,width,length):
        self.name = name
        self.width = width
        self.length = length

    @Lazyproperty   #area = Lazyproperty(area)
    def area(self):
        return self.width * self.length

r1 = Room('lary',1,1)
print(r1.area)
自定义property
class Lazyproperty():
    def __init__(self,func):
        self.func = func

    def __get__(self, instance, owner):
        print('这是我们自己定制的静态属性,r1.area实际是要执行r1.area()')
        if instance is None:
            return self
        else:
            print('--->')
            value = self.func(instance)
            setattr(instance,self.func.__name__,value) #计算一次就缓存到实例的属性字典中
            print('第一次访问__get__方法')
            return value

class Room():
    def __init__(self,name,width,length):
        self.name = name
        self.width = width
        self.length = length

    @Lazyproperty   #area = Lazyproperty(area)
    def area(self):
        return self.width * self.length

r1 = Room('lary',1,2)
print(r1.area) #先从自己的属性字典找,没有再去类的中找,然后触发了area的__get__方法
print(r1.area) #先从自己的属性字典找,找到了,是上次计算的结果,这样就不用每执行一次都去计算


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

    def __get__(self, instance, owner):
        print('这是我们自己定制的静态属性,r1.area实际是要执行r1.area()')
        if instance is None:
            return self
        else:
            print('第n次访问get方法')
            value = self.func(instance)
            #setattr(instance,self.func.__name__,value) #计算一次就缓存到实例的属性字典中
            instance.__dict__[self.func.__name__]=value
            return value

    def __set__(self, instance, value):
        print('hhhhh')

class Room():
    def __init__(self,name,width,length):
        self.name = name
        self.width = width
        self.length = length

    @Lazyproperty   #area = Lazyproperty(area)
    def area(self):
        return self.width * self.length

r1 = Room('lary',1,2)
print(r1.area)
print(r1.area)
print(r1.area)#缓存功能失效每次都去找描述符了.因为描述符实现了set方法,它由非数据描述符变成了数据描述符,数据描述符比实例属性有更高的优先级,因而所有的属性操作都去找描述符了
实现缓存
#自定义@classmethod

class Animal():
    feature = 'live'
    def __init__(self,name):
        self.name = name

    @classmethod
    def feature_animal(cls):
        return cls.feature

a1 = Animal.feature_animal()
print(a1)

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

    def __get__(self, instance, owner):
        def feedback():
            print('在这里可以加功能哦')
            return self.func(owner)
        return feedback


class Animal():
    feature = 'live'

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

    @lazyClassMethod
    def feature_animal(cls):
        return cls.feature

a1 = Animal('cat')
res=a1.feature_animal()
print(res)

#带参数的自定义@classmethod
class lazyClassMethod():
    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 Animal():
    feature = 'live'

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

    @lazyClassMethod
    def feature_animal(cls,msg):
        print('animal is %s:%s'%(cls.feature,msg))

a1 = Animal('cat')
res=a1.feature_animal('so cute')
自定义classmethod
#自定义staticmethod

class Animal():

    @staticmethod
    def eat(food,water):
        print('animal is eating %s drink %s'%(food,water))

a1 = Animal()
a1.eat('meat','water')

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

    def __get__(self, instance, owner):
        def feedback(*args,**kwargs):
            return self.func(*args,**kwargs)
        return feedback


class Animal():

    @lazyStaticmethod    #eat = lazyStaticmethod(eat)
    def eat(food,water):
        print('animal is eating %s drink %s'%(food,water))

a1 = Animal()
a1.eat('food','water')
自定义staticmethod
原文地址:https://www.cnblogs.com/iamluoli/p/9885128.html