Python之路(第二十七篇) 物件導向進階:內建方法、描述符

來源:互聯網
上載者:User

標籤:span   exp   編程   實現   init   列印   open()   %s   優先   

一、 __call__

 

對象後面加括弧,觸發執行類下面的__call__方法。

建立對象時,對象 = 類名() ;而對於 __call__ 方法的執行是由對象後加括弧觸發的,即:對象() 或者 類()()

  class Foo:  ?      def __call__(self, *args, **kwargs):          print("我執行啦")  ?  f = Foo()  f()  #對象加括弧調用執行類下的__call__方法  #輸出結果 我執行啦 

  

 

二、 __next____iter__實現迭代器協議

 

迭代器協議是指:對象必須提供一個next方法,執行該方法要麼返回迭代中的下一項,要麼就引起一個StopIteration異常,以終止迭代 (只能往後走不能往前退)

 

可迭代對象執行obj.__iter__()得到的結果就是迭代器對象。

在類中,如果有__iter____next__內建方法,那麼就構成了迭代器。

例子

  
  class Foo:  ?      def __init__(self,n):          self.n = n  ?      def __iter__(self):          return self  #執行個體本身就是迭代對象,故返回自己  ?      def __next__(self):          if self.n >10:              raise StopIteration   #如果超過10就報StopIteration 錯誤          self.n = self.n + 1          return self.n  ?  f = Foo(7)  for i in f:  #for迴圈自動調用__next__方法,實現了迭代取值      print(i)  

  

例子2

輸出100內的斐契那波數列

  
  class F:  ?      def __init__(self):          self.a = 0          self.b = 1  ?      def __iter__(self):          return self  ?      def __next__(self):          self.a ,self.b = self.b , self.a + self.b          if self.a > 100:              raise StopIteration          return self.a  ?  f = F()  for i in f:      print(i) 

  

 

三、描述符( __get__,__set__,__delete__)

描述符(descriptor):

1、描述符本質

就是一個新式類,在這個新式類中,至少實現了__get__(),__set__(),__delete__()中的一個,這也被稱為描述符協議。__get__():調用一個屬性時,觸發__set__():為一個屬性賦值時,觸發__delete__():採用del刪除屬性時,觸發

 

2、描述符的作用

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

描述符是在另外一個類的類屬性進行定義的,描述符在一個類的類屬性__dict__字典裡

 

例子1

  class Foo:  ?      def __get__(self, instance, owner):          print("執行了__get__")  ?      def __set__(self, instance, value):          print("執行了__set__")  ?      def __delete__(self, instance):          print("執行了__delete__")  ?  ?  class Bar:      x = Foo()  ?      def __init__(self,name):          self.name = name  ?  ?  b = Bar("nick")  b.x     #調用執行描述符裡的__get__方法  print(b.x)  #  b.x = 1  # 調用執行描述符裡的__set__方法  print(b.__dict__)  del b.x  #調用執行描述符裡的__delete__方法  print(b.__dict__)

  

 

輸出結果

  執行了__get__  執行了__get__  None  執行了__set__  {‘name‘: ‘nick‘}  執行了__delete__  {‘name‘: ‘nick‘}

  

 

例子2

    #描述符Str  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 People:      name=Str()      age=Int()      def __init__(self,name,age): #name被Str類代理,age被Int類代理,          self.name=name          self.age=age  ?  #何地?:定義成另外一個類的類屬性  ?  #何時?:且看下列示範  ?  p1=People(‘alex‘,18)  ?  #描述符Str的使用  p1.name  p1.name=‘egon‘  del p1.name  ?  #描述符Int的使用  p1.age  p1.age=18  del p1.age  ?  #我們來瞅瞅到底發生了什麼  print("__p1.__dict__",p1.__dict__)  print(People.__dict__)  ?  #補充  print(type(p1) == People) #type(obj)其實是查看obj是由哪個類執行個體化來的  print(type(p1).__dict__ == People.__dict__)

  

輸出結果

    Str設定...  Int設定...  Str調用  Str設定...  Str刪除...  Int調用  Int設定...  Int刪除...  __p1.__dict__ {}  {‘__module__‘: ‘__main__‘, ‘name‘: <__main__.Str object at 0x021C6850>, ‘age‘: <__main__.Int object at 0x021C6870>, ‘__init__‘: <function People.__init__ at 0x021C5DB0>, ‘__dict__‘: <attribute ‘__dict__‘ of ‘People‘ objects>, ‘__weakref__‘: <attribute ‘__weakref__‘ of ‘People‘ objects>, ‘__doc__‘: None}  True  True 

  

3、描述符分兩種

(1) 資料描述符:至少實現了__get__()__set__()

 

  ?  class Foo:      def __set__(self, instance, value):          print(‘set‘)      def __get__(self, instance, owner):          print(‘get‘)

  

(2) 非資料描述符:沒有實現__set__()

    class Foo:      def __get__(self, instance, owner):          print(‘get‘)

  

 注意:非資料描述符一般是只有__get__,如果保留__delete__執行會報錯。

4、 注意事項:

 

(1)描述符本身應該定義成新式類,被代理的類也應該是新式類(python3中全部是新式類)

(2)必須把描述符定義成另外一個類的類屬性,不能為定義到建構函式中,

(3)要嚴格遵循該優先順序,優先順序由高到底分別是

a.類屬性b.資料描述符c.執行個體屬性d.非資料描述符e.找不到的屬性觸發__getattr__()

 

例子1

  
  class Foo:  ?      def __get__(self, instance, owner):          print("執行了__get__")  ?      def __set__(self, instance, value):          print("執行了__set__")  ?      def __delete__(self, instance):          print("執行了__delete__")  ?  class People:  ?      name = Foo()  ?      def __init__(self,name):          self.name = name  ?  ?  p = People("nick")  People.name  = "nick"  #調用執行了描述符的__set__方法,這一步類屬性由之前的描述符被定義成另外一個字串,  # 所以下面再次調用就無法再次使用描述符了  People.name     ?  #可以得出結論,類屬性的優先順序大於資料描述符

  

 

例子2

  
  class Foo:  ?      def __get__(self, instance, owner):          print("執行了__get__")  ?      def __set__(self, instance, value):          print("執行了__set__")  ?      def __delete__(self, instance):          print("執行了__delete__")  ?  class People:  ?      name = Foo()  ?      def __init__(self,name):          self.name = name  ?  ?  p = People("nick")  #執行個體化對象,調用資料描述符的__set__,  # 但是由於描述符的__set__只是執行了列印操作,什麼都沒做,所以p對象的__dict__什麼都沒有  p.name = "nicholas"  print(p.__dict__) #輸出的結果為空白  ?  #因此可以得出結論,資料描述符的優先順序大於執行個體屬性(字典操作)

  

 

例子3

  class Foo(object):      def __init__(self):          pass  ?      def __get__(self, instance, owner):          print("執行了__get__")  ?  class People(object):  ?      name = Foo("x")  ?      def __init__(self,name,age):          self.name = name          self.age = age  ?  ?  ?  p = People("nick",18)  #執行個體化對象,這裡由於是非資料描述符,優先順序低於執行個體屬性,  # 所以這裡直接設定了執行個體屬性,而不再調用描述符  print(p.name)  #列印直接輸出執行個體屬性  print(p.__dict__)  #輸出的結果:{‘name‘: ‘nick‘, ‘age‘: 18}  ?  #因此可以得出結論,執行個體屬性的優先順序大於非資料描述符

  

 

例子4

    class Foo(object):      def __init__(self,name2):          self.name2 = name2  ?      def __get__(self, instance, owner):          print("執行了__get__")  ?  ?  class People(object):  ?      name = Foo("x")  ?      def __init__(self,name,age):          self.name = name          self.age = age  ?      def __getattr__(self, item):          print("__getattr__")  ?  ?  p = People("nick",18)  #執行個體化對象,這裡由於是非資料描述符,優先順序低於執行個體屬性,  # 所以這裡直接設定了執行個體屬性,而不再調用描述符  print(p.name)  print(p.sex)  #調用不存在的屬性執行了__getattr__  print(p.__dict__)  #輸出的結果:{‘name‘: ‘nick‘, ‘age‘: 18}

  

 

5、描述符的應用

例子1

  
  class Type:  ?      def __init__(self,key,expect_type):          self.key = key          self.expect_type = expect_type  ?      def __get__(self, instance, owner):          print("執行__get__方法")          print(self)                #這裡的self就是type類的對象          print(instance)            #這裡的instance就是傳入的People類的對象          print("執行__get__方法")          return  instance.__dict__[self.key]   #通過instance的字典擷取對象的屬性值  ?      def __set__(self, instance, value):          print("執行__set__方法")          instance.__dict__[self.key] = value   #instance是另一個類的對象,這裡要設定對象的屬性字典  ?      def __delete__(self, instance):          print("執行__delete__方法")          instance.__dict__.pop(self.key)  #刪除對象的屬性  ?  class People:      name = Type("name",str)      age = Type("age",int)  ?      def __init__(self,name,age):          self.name = name          self.age = age  ?  p1 = People("nick",18)  #調用2次描述符,對對象的字典進行設定  print(p1.name)  #通過資料描述符擷取對象的屬性值  print(p1.__dict__)  p1.age = 20  #調用描述符對對象進行設定  print(p1.__dict__)

  

輸出結果

  
  執行__set__方法  執行__set__方法  執行__get__方法  <__main__.Type object at 0x004CB4F0>  <__main__.People object at 0x02106DF0>  執行__get__方法  nick  {‘name‘: ‘nick‘, ‘age‘: 18}  執行__set__方法  {‘name‘: ‘nick‘, ‘age‘: 20}

  


?

 

例子2

 

  
  class Type:  ?      def __init__(self,key,expect_type):          self.key = key          self.expect_type = expect_type  ?      def __get__(self, instance, owner):          print("執行__get__方法")          print(self)                #這裡的self就是type類的對象          print(instance)            #這裡的instance就是傳入的People類的對象          print("執行__get__方法")          return  instance.__dict__[self.key]   #通過instance的字典擷取對象的屬性值  ?      def __set__(self, instance, value):          print("執行__set__方法")          if not isinstance(value,self.expect_type):              print("您輸入的%s不是%s"%(self.key,self.expect_type))              raise TypeError          instance.__dict__[self.key] = value   #instance是另一個類的對象,這裡要設定對象的屬性字典  ?      def __delete__(self, instance):          print("執行__delete__方法")          instance.__dict__.pop(self.key)  #刪除對象的屬性  ?  class People:      name = Type("name",str)      age = Type("age",int)  ?      def __init__(self,name,age):          self.name = name          self.age = age  ?  p1 = People("nick",18)  #調用2次描述符,對對象的字典進行設定  print(p1.name)  #通過資料描述符擷取對象的屬性值  print(p1.__dict__)  p1.age = 20  #調用描述符對對象進行設定  print(p1.__dict__)  # p1.name = 11  #通過描述符的if not isinstance(value,self.expect_type)判斷屬性的類型  ?  # p2 = People(88,18)  #通過描述符的if not isinstance(value,self.expect_type)判斷屬性的類型

  

 

 

四、 __enter____exit__

開啟檔案操作用 with open() as f操作,這叫做上下文管理協議,即with語句,為了讓一個對象相容with語句,必須在這個對象的類中聲明__enter____exit__方法。

__enter__(self):當with開始啟動並執行時候觸發此方法的運行

__exit__(self, exc_type, exc_val, exc_tb):當with運行結束之後觸發此方法的運行

exc_type如果拋出異常,這裡擷取異常的類型

exc_val如果拋出異常,這裡顯示異常內容

exc_tb如果拋出異常,這裡顯示所在位置

 

用途或者說好處:

1.使用with語句的目的就是把代碼塊放入with中執行,with結束後,自動完成清理工作,無須手動幹預

2.在需要管理一些資源比如檔案,網路連接和鎖的編程環境中,可以在__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__")        print(exc_type)        print(exc_val)        print(exc_tb)        print("執行__exit__2222")with OPEN("a.txt") as f:    print(f)  #執行列印__enter__內建方法,同時列印內建方法返回的結果#with 語句結束時執行__exit__方法,沒有錯誤則列印None,有錯誤則列印錯誤的資訊print("上下文管理協議")

  

 

Python之路(第二十七篇) 物件導向進階:內建方法、描述符

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.