面向物件_描述符
阿新 • • 發佈:2018-10-31
描述符就是一個新式類,在這個新式類中,至少實現了__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