Python之路(第二十六篇) 物件導向進階:內建方法

來源:互聯網
上載者:User

標籤:and   getattr   尋找   需要   for   異常處理   類執行個體化   統一   注意   

一、 __getattribute__ object.__getattribute__(self, name)

無條件被調用,通過執行個體訪問屬性。如果class中定義了__getattr__(),則__getattr__()不會被調用(除非顯示調用或引發AttributeError異常)

 

  
  class Foo:  ?      def __init__(self,x):          self.x = x  ?      def __getattr__(self, item):          print("執行__getattr__")  ?      def __getattribute__(self, item):          print("執行__getattribute__")          # raise AttributeError("不存在的異常")  ?  f = Foo("nick")  print(f.x)  print(f.xxxx)#這裡本來調用後應該在__getattribute__中產生AttributeError,然後調用__getattr__,但在#這裡__getattribute__被重寫了,沒有產生AttributeError,所以就沒調用__getattr__

  

輸出結果

    執行__getattribute__  None  執行__getattribute__  None 

  

object.__getattr__(self, name)

當一般位置找不到attribute的時候,會調用getattr,返回一個值或AttributeError異常。

  
  class Foo:  ?      def __init__(self,x):          self.x = x  ?      def __getattr__(self, item):          print("執行__getattr__")  ?      # def __getattribute__(self, item):      #     print("執行__getattribute__")          # raise AttributeError("不存在的異常")  ?  f = Foo("nick")  print(f.x)  print(f.xxxx) #這裡是執行了新定義的__getattr__方法,並沒有直接報錯

  

輸出結果

  
  nick  執行__getattr__  None 

  

每次通過執行個體訪問屬性,都會經過__getattribute__函數。而當屬性不存在時,仍然需要訪問__getattribute__,不過接著要訪問__getattr__。這就好像是一個異常處理函數。 需要注意的是,當使用類訪問不存在的變數是,不會經過__getattr__函數。

當屬性存在時,經過__getattribute__函數,返回屬性的值。

 

 

二、 __setitem__,__getitem,__delitem__

 

在類中,對象以object["key"]形式訪問屬性或方法時調用item系列內建方法,對象以object.屬性(以點的形式)調用屬性或方法時調用attr系列內建方法。

 

  
  class Foo:  ?      def __init__(self,name):          self.name = name  ?      def __getitem__(self, item):          print("執行__getitem__")          return self.__dict__[item]  ?      def __setitem__(self, key, value):          print("執行__setitem__")          self.__dict__[key] = value  ?      def __delitem__(self, key):          print("執行__delitem__")          self.__dict__.pop(key)  ?  f = Foo("nick")  print(f["name"])  #擷取屬性, 執行__getitem__方法  f["age"] = 18  #設定屬性,執行__setitem__方法  print(f.__dict__)  del f["name"]  #刪除屬性,執行__delitem__方法  print(f.__dict__)

  

輸出結果

    執行__getitem__  nick  執行__setitem__  {‘name‘: ‘nick‘, ‘age‘: 18}  執行__delitem__  {‘age‘: 18}

  

 

三、 __str__,__repr__, __format__

__str__是控制改變對象的字串顯示

print(obj)/‘%s‘%obj/str(obj)時,實際上是內部調用了obj.__str__方法,如果str方法有,那麼他返回的必定是一個字串,如果沒有__str__方法,會先找本類中的__repr__方法,再沒有再找父類(object)中的__str__

例子1

  class Foo:      def __str__(self):          return "控制列印對象時的顯示形式"  ?  f = Foo()  print(f) #輸出結果:  控制列印對象時的顯示形式

  

分析:這裡列印對象直接調用了被重新定義的__str__方法

例子2

  class Foo:      def __str__(self):          return "控制列印對象時的顯示形式"  ?  f = Foo()  a = str(f)  print(a) #輸出結果:  控制列印對象時的顯示形式

  

分析:這裡的str(object)也是調用了__str__方法

例子3

  
  class Foo:      def __str__(self):          return "控制列印對象時的顯示形式"  ?  f = Foo()  print("%s"%f) #輸出結果:  控制列印對象時的顯示形式

  

分析:這裡的“%s”%f也是調用了__str__方法

 

__repr__也是顯示對象的名字方式,用repr(object)或者“%r”%object會調用__repr__方法

repr 是str的備胎,但str不能做repr的備胎

 

例子4

  class Foo:  ?      def __str__(self):          return "控制列印對象時的顯示形式"  ?      def __repr__(self):          print("在執行repr")          return "repr Foo"      pass  f = Foo()  print("%r"%f)   print(f)

  

輸出結果

  
  在執行repr  repr Foo  控制列印對象時的顯示形式

  

分析:這裡print("%r"%f) 調用重新定義的__repr__方法,print(f)調用__str__方法

例子5

  
  class Foo:  ?      def __str__(self):          return "控制列印對象時的顯示形式"  ?      def __repr__(self):          print("在執行repr")          return "repr Foo"      pass  f = Foo()  print(repr(f)) 

  

輸出結果

  在執行repr  repr Foo

  

例子6

  
  class Foo:  ?      # def __str__(self):      #     return "控制列印對象時的顯示形式"  ?      def __repr__(self):          print("在執行repr")          return "repr Foo"      pass  f = Foo()  print(f)

  

輸出結果

    在執行repr  repr Foo

  

分析:如果要調用__str__方法,首先在本類中尋找有沒有__str__方法,如果沒有就找本類有沒有__repr__方法,如果有就執行__repr__方法,不再借用父類的__str__方法。所以repr 是str的備胎,但str不能做repr的備胎。

 

例子7

  
  class Foo:  ?      def __str__(self):          return "控制列印對象時的顯示形式"  ?      # def __repr__(self):      #     print("在執行repr")      #     return "repr Foo"      pass  f = Foo()  print(repr(f)) #輸出結果 <__main__.Foo object at 0x002EB550>

  

分析:要調用__repr__方法,本類沒有,直接借用父類的__repr__方法,而不執行__str__方法。所以repr 是str的備胎,但str不能做repr的備胎。

 

__format__

自定製格式化字串__format__,

 

    format_dic = {      "ymd":"{0.year}{0.month}{0.day}",      "dmy":"{0.day}:{0.month}:{0.year}",      "mdy":"{0.month}-{0.day}-{0.year}"  }  ?  class Foo:  ?      def __init__(self,year,month,day):          self.year = year          self.month = month          self.day = day  ?      def __format__(self, format_spec):          if not format_spec or format_spec not in format_dic:              format_spec = "ymd"          fmt = format_dic[format_spec]          return fmt.format(self)  ?  f = Foo(2018,8,20)  print(format(f))  print(format(f,"dmy"))  print(format(f,"mdy"))  print("{:mdy}".format(f))

  

輸出結果

  
  2018820  20:8:2018  8-20-2018  8-20-2018

  

分析:調用format方法自定製字串格式化,這裡調用__format__方法

 

四、 __slots__

__slots__:其實就是將類中的名稱鎖定,執行個體化對象,只可以賦值和調用,不可以刪除屬性和增加新的屬性

1、__slots__是什麼:是一個類變數,變數值可以是列表,元祖,或者可迭代對象,也可以是一個字串(也就是意味著所有執行個體只有一個資料屬性)

2、使用點來訪問屬性本質就是在訪問類或者對象的__dict__屬性字典(類的字典是共用的,而每個執行個體的是獨立的)

3、字典會佔用大量記憶體,如果你有一個屬性很少的類,但是有很多執行個體,為了節省記憶體可以使用__slots__取代執行個體的__dict__當你定義__slots__後,__slots__就會為執行個體使用一種更加緊湊的內部表示。執行個體通過一個很小的固定大小的數組來構建,而不是為每個執行個體定義一個字典,這跟元組或列表很類似。在__slots__中列出的屬性名稱在內部被映射到這個數組的指定小標上。使用__slots__一個不好的地方就是我們不能再給執行個體添加新的屬性了,只能使用在__slots__中定義的那些屬性名稱。

4、應用情境:當執行個體化幾萬個對象的時候,每個對象都會產生一個名稱空間__dict__,而每一個名稱空間都會各自佔用一個記憶體,造成記憶體的浪費,用 __slots__,不用再產生每個對象的 dict 了,省記憶體,對象的 dict 都統一用類的 dict,屬性都是用 slots 給定義的

  
  class Foo:  ?      __slots__ = ["name","age"]  #在類中定義屬性"name"、"age"  ?  f1 = Foo()  f1.name = "nick" #首先要進行賦值,不賦值調用會出錯  print(f1.name)  # f1.gender = "female"  # 新增__slots__之外的屬性會報錯  # del f1.age   #刪除屬性也會報錯

  

 

五、 __doc__

__doc__顯示文檔注釋資訊,文檔注釋資訊無法繼承給子類。

例子

  
  class Foo:      """      我是文檔注釋      """      pass  ?  f = Foo()  print(f.__doc__) #輸出結果 :    我是文檔注釋

  

例子2

  
  class Foo:      """      我是文檔注釋      """      pass  ?  class Son(Foo):      pass  ?  s = Son()  print(s.__doc__)  #輸出結果:None 

  

六、 __module____class__

 

 __module__ 表示當前操作的對象在那個模組

  __class__ 表示當前操作的對象的類是什麼

例子1

  
  class Foo:  ?      def __init__(self,name):          self.name = name  ?  f = Foo("nick")  print(f.__module__) #當執行檔案為當前檔案時,__module__ = __main__  #輸出結果:__main__  print(f.__class__)  #輸出當前操作的類名是什麼  #輸出結果:<class ‘__main__.Foo‘>

  

例子2

檔案結構

  ├── index.py #執行檔案
│ ├── test
│ │   ├── a.py  

a.py內容:

  
  class Bar:      def __init__(self):          self.name = "nicholas"

  

 

index.py內容:

 from test.a import  Bar  ?  class Foo:  ?      def __init__(self,name):          self.name = name  ?  b = Bar()  print(b.name)  #輸出結果 nicholas  print(b.__module__)  #輸出結果 test.a ,表示當前操作的類在test.a下  print(b.__class__)  #表示當前操作的類名是<class ‘test.a.Bar‘> 

  

七、 __del__析構方法

析構方法,當對象在記憶體中被釋放時,自動觸發執行。即清除釋放記憶體的方法。

這個是在回收執行個體化對象時觸發執行的方法,刪除對象的屬性不會觸發__del__方法

註:此方法一般無須定義,因為Python是一門進階語言,程式員在使用時無需關心記憶體的分配和釋放,因為此工作都是交給Python解譯器來執行,所以,解構函式的調用是由解譯器在進行記憶體回收時自動觸發執行的。

 

例子1

  
  class Foo:      def __init__(self,name):          self.name = name  ?      def __del__(self):          print("我執行啦!")  ?  f = Foo("nick")  print("-----1")  del f.name  print("-----2")   #這裡是先執行print("-----1")然後執行print("-----2") ,  # 執行del f.name只是刪除對象字典的"name"屬性,並不會觸發__del__方法

  

輸出結果

  
  -----1  -----2  我執行啦!

  

 

 

例子2

  
  class Foo:      def __init__(self,name):          self.name = name  ?      def __del__(self):          print("我執行啦!")  ?  f = Foo("nick")  print("-----1")  del f   #刪除對象才能調用__del__,這裡既執行了__del__方法,又刪除了對象  print("-----2")   

  

輸出結果

    -----1  我執行啦!  -----2 

  

例子3

  
  class Foo:      def __init__(self,name):          self.name = name  ?      def __del__(self):          print("我執行啦!")  ?  f = Foo("nick")  import time  time.sleep(2)

  

輸出結果


 我執行啦!

  

分析:這裡在程式結束時,python解譯器自動回收記憶體,執行了__del__方法

 

典型的應用情境:

建立資料庫類,用該類執行個體化出資料庫連結化物件,對象本身是存放於使用者空間記憶體中,而連結則是由作業系統管理的,存放於核心空間記憶體中

當程式結束時,python只會回收自己的記憶體空間,即使用者態記憶體,而作業系統的資源則沒有被回收,這就需要我們定製__del__,在對象被刪除前向作業系統發起關閉資料庫連結的系統調用,回收資源。

 

例子

  
  class Foo:  ?      def __del__(self):          print("我執行了")  ?  f = Foo()  f.file  = open("test.txt")   #開啟檔案,在作業系統中開啟了一個檔案,  # 拿到了檔案操作符存在了記憶體中  del f.file   #刪除了記憶體中的檔案操作符,但是並未關閉作業系統中的檔案,  # 這時需要在__del__中加入定製f.file.close(),關閉作業系統的檔案  print("-----1")  #最後程式結束python解譯器自動執行記憶體回收,執行了__del__方法

  

輸出結果

    -----1  我執行了 

  

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.