1. 程式人生 > >面向物件_描述符

面向物件_描述符

  描述符就是一個新式類,在這個新式類中,至少實現了__get__(),__set__(),__delete__()中的一個,這也被稱為描述符協議

__get__():呼叫一個屬性時觸發
__set__():為一個屬性賦值時觸發
__delete__():使用delete刪除屬性時觸發

觸發

  描述符的作用是用來代理另外一個類的屬性的(必須把描述符定義成這個類的類屬性,不能定義到建構函式中)

#描述符類產生的例項進行屬性操作並不會觸發三個方法的執行
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類代理,age被Int類代理, 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)

 

123