面向對象學習目錄 1 面向對象介紹 2 類、實例、屬性、方法詳解 3 面向過程與面向對象進一步比較 4 類與對象 5 屬性查找與綁定方法 6 小結 7 繼承與派生 8 組合 9 抽象類 10 多態 11 封裝 12 綁定方法與非綁定方法 13 內置方法(上) 14 內置方法(中)之描述符 15 內置 ...
面向對象學習目錄
七、再看property
一個靜態屬性property本質就是實現了get,set,delete三種方法 用法一1 class Foo: 2 @property 3 def AAA(self): 4 print('get的時候運行我啊') 5 6 @AAA.setter 7 def AAA(self,value): 8 print('set的時候運行我啊') 9 10 @AAA.deleter 11 def AAA(self): 12 print('delete的時候運行我啊') 13 14 #只有在屬性AAA定義property後才能定義AAA.setter,AAA.deleter 15 f1=Foo() 16 f1.AAA 17 f1.AAA='aaa' 18 del f1.AAA
用法二
1 class Foo: 2 def get_AAA(self): 3 print('get的時候運行我啊') 4 5 def set_AAA(self,value): 6 print('set的時候運行我啊') 7 8 def delete_AAA(self): 9 print('delete的時候運行我啊') 10 11 AAA=property(get_AAA,set_AAA,delete_AAA) #內置property三個參數與get,set,delete一一對應 12 13 f1=Foo() 14 f1.AAA 15 f1.AAA='aaa' 16 del f1.AAA
怎麼用?
案例一1 class Goods: 2 def __init__(self): 3 # 原價 4 self.original_price = 100 5 # 折扣 6 self.discount = 0.8 7 8 @property 9 def price(self): 10 # 實際價格 = 原價 * 折扣 11 new_price = self.original_price * self.discount 12 return new_price 13 14 @price.setter 15 def price(self, value): 16 self.original_price = value 17 18 @price.deleter 19 def price(self): 20 del self.original_price 21 22 23 obj = Goods() 24 obj.price # 獲取商品價格 25 obj.price = 200 # 修改商品原價 26 print(obj.price) 27 del obj.price # 刪除商品原價
案例二
1 #實現類型檢測功能 2 3 #第一關: 4 class People: 5 def __init__(self,name): 6 self.name=name 7 8 @property 9 def name(self): 10 return self.name 11 12 # p1=People('alex') 13 #property自動實現了set和get方法屬於數據描述符,比實例屬性優先順序高,所以你這面寫會觸發property內置的set,拋出異常 14 15 16 #第二關:修訂版 17 18 class People: 19 def __init__(self,name): 20 self.name=name #實例化就觸發property 21 22 @property 23 def name(self): 24 # return self.name #無限遞歸 25 print('get------>') 26 return self.DouNiWan 27 28 @name.setter 29 def name(self,value): 30 print('set------>') 31 self.DouNiWan=value 32 33 @name.deleter 34 def name(self): 35 print('delete------>') 36 del self.DouNiWan 37 38 p1=People('alex') #self.name實際是存放到self.DouNiWan里 39 print(p1.name) 40 print(p1.name) 41 print(p1.name) 42 print(p1.__dict__) 43 44 p1.name='egon' 45 print(p1.__dict__) 46 47 del p1.name 48 print(p1.__dict__) 49 50 51 #第三關:加上類型檢查 52 class People: 53 def __init__(self,name): 54 self.name=name #實例化就觸發property 55 56 @property 57 def name(self): 58 # return self.name #無限遞歸 59 print('get------>') 60 return self.DouNiWan 61 62 @name.setter 63 def name(self,value): 64 print('set------>') 65 if not isinstance(value,str): 66 raise TypeError('必須是字元串類型') 67 self.DouNiWan=value 68 69 @name.deleter 70 def name(self): 71 print('delete------>') 72 del self.DouNiWan 73 74 p1=People('alex') #self.name實際是存放到self.DouNiWan里 75 p1.name=1
八、__setitem__,__getitem__,__delitem__
1 class Foo: 2 def __init__(self,name): 3 self.name=name 4 5 def __getitem__(self, item): 6 print(self.__dict__[item]) 7 8 def __setitem__(self, key, value): 9 self.__dict__[key]=value 10 def __delitem__(self, key): 11 print('del obj[key]時,我執行') 12 self.__dict__.pop(key) 13 def __delattr__(self, item): 14 print('del obj.key時,我執行') 15 self.__dict__.pop(item) 16 17 f1=Foo('sb') 18 f1['age']=18 19 f1['age1']=19 20 del f1.age1 21 del f1['age'] 22 f1['name']='alex' 23 print(f1.__dict__)
九、__str__,__repr__,__format__
改變對象的字元串顯示__str__,__repr__ 自定製格式化字元串__format__1 format_dict={ 2 'nat':'{obj.name}-{obj.addr}-{obj.type}',#學校名-學校地址-學校類型 3 'tna':'{obj.type}:{obj.name}:{obj.addr}',#學校類型:學校名:學校地址 4 'tan':'{obj.type}/{obj.addr}/{obj.name}',#學校類型/學校地址/學校名 5 } 6 7 class School: 8 def __init__(self,name,addr,type): 9 self.name=name 10 self.addr=addr 11 self.type=type 12 13 def __repr__(self): 14 return 'School(%s,%s)' %(self.name,self.addr) 15 def __str__(self): 16 return '(%s,%s)' %(self.name,self.addr) 17 18 def __format__(self, format_spec): 19 # if format_spec 20 if not format_spec or format_spec not in format_dict: 21 format_spec='nat' 22 fmt=format_dict[format_spec] 23 return fmt.format(obj=self) 24 25 s1=School('oldboy1','北京','私立') 26 print('from repr: ',repr(s1)) 27 print('from str: ',str(s1)) 28 print(s1) 29 30 ''' 31 str函數或者print函數--->obj.__str__() 32 repr或者互動式解釋器--->obj.__repr__() 33 如果__str__沒有被定義,那麼就會使用__repr__來代替輸出 34 註意:這倆方法的返回值必須是字元串,否則拋出異常 35 ''' 36 print(format(s1,'nat')) 37 print(format(s1,'tna')) 38 print(format(s1,'tan')) 39 print(format(s1,'asfdasdffd'))
自定義format練習
1 date_dic={ 2 'ymd':'{0.year}:{0.month}:{0.day}', 3 'dmy':'{0.day}/{0.month}/{0.year}', 4 'mdy':'{0.month}-{0.day}-{0.year}', 5 } 6 7 class Date: 8 def __init__(self,year,month,day): 9 self.year=year 10 self.month=month 11 self.day=day 12 13 def __format__(self, format_spec): 14 if not format_spec or format_spec not in date_dic: 15 format_spec='ymd' 16 fmt=date_dic[format_spec] 17 return fmt.format(self) 18 19 d1=Date(2016,12,29) 20 print(format(d1)) 21 print('{:mdy}'.format(d1))
issubclass和isinstance
1 class A: 2 pass 3 4 class B(A): 5 pass 6 7 print(issubclass(B,A)) #B是A的子類,返回True 8 9 a1=A() 10 print(isinstance(a1,A)) #a1是A的實例
十、slots
slots使用1 ''' 2 1.__slots__是什麼:是一個類變數,變數值可以是列表,元祖,或者可迭代對象,也可以是一個字元串(意味著所有實例只有一個數據屬性) 3 2.引子:使用點來訪問屬性本質就是在訪問類或者對象的__dict__屬性字典(類的字典是共用的,而每個實例的是獨立的) 4 3.為何使用__slots__:字典會占用大量記憶體,如果你有一個屬性很少的類,但是有很多實例,為了節省記憶體可以使用__slots__取代實例的__dict__ 5 當你定義__slots__後,__slots__就會為實例使用一種更加緊湊的內部表示。實例通過一個很小的固定大小的數組來構建,而不是為每個實例定義一個 6 字典,這跟元組或列表很類似。在__slots__中列出的屬性名在內部被映射到這個數組的指定小標上。使用__slots__一個不好的地方就是我們不能再給 7 實例添加新的屬性了,只能使用在__slots__中定義的那些屬性名。 8 4.註意事項:__slots__的很多特性都依賴於普通的基於字典的實現。另外,定義了__slots__後的類不再 支持一些普通類特性了,比如多繼承。大多數情況下,你應該 9 只在那些經常被使用到 的用作數據結構的類上定義__slots__比如在程式中需要創建某個類的幾百萬個實例對象 。 10 關於__slots__的一個常見誤區是它可以作為一個封裝工具來防止用戶給實例增加新的屬性。儘管使用__slots__可以達到這樣的目的,但是這個並不是它的初衷。 更多的是用來作為一個記憶體優化工具。 11 12 ''' 13 class Foo: 14 __slots__='x' 15 16 17 f1=Foo() 18 f1.x=1 19 f1.y=2 #報錯 20 print(f1.__slots__) #f1不再有__dict__ 21 22 class Bar: 23 __slots__=['x','y'] 24 25 n=Bar() 26 n.x,n.y=1,2 27 n.z=3 #報錯
刨根問底
1 class Foo: 2 __slots__=['name','age'] 3 4 f1=Foo() 5 f1.name='alex' 6 f1.age=18 7 print(f1.__slots__) 8 9 f2=Foo() 10 f2.name='egon' 11 f2.age=19 12 print(f2.__slots__) 13 14 print(Foo.__dict__) #f1與f2都沒有屬性字典__dict__了,統一歸__slots__管,節省記憶體
十一、__next__和__iter__實現迭代器協議
簡單示範1 class Foo: 2 def __init__(self,x): 3 self.x=x 4 5 def __iter__(self): 6 return self 7 8 def __next__(self): 9 n=self.x 10 self.x+=1 11 return self.x 12 13 f=Foo(3) 14 for i in f: 15 print(i)
1 class Foo: 2 def __init__(self,start,stop): 3 self.num=start 4 self.stop=stop 5 def __iter__(self): 6 return self 7 def __next__(self): 8 if self.num >= self.stop: 9 raise StopIteration 10 n=self.num 11 self.num+=1 12 return n 13 14 f=Foo(1,5) 15 16 from collections import Iterable,Iterator 17 print(isinstance(f,Iterator)) 18 19 for i in Foo(1,5): 20 print(i)
練習:簡單模擬range,加上步長
1 class Range: 2 def __init__(self,n,stop,step): 3 self.n=n 4 self.stop=stop 5 self.step=step 6 7 def __next__(self): 8 if self.n >= self.stop: 9 raise StopIteration 10 x=self.n 11 self.n+=self.step 12 return x 13 14 def __iter__(self): 15 return self 16 17 for i in Range(1,7,3): # 18 print(i)
斐波那契數列
1 class Fib: 2 def __init__(self): 3 self._a=0 4 self._b=1 5 6 def __iter__(self): 7 return self 8 9 def __next__(self): 10 self._a,self._b=self._b,self._a + self._b 11 return self._a 12 13 f1=Fib() 14 15 print(f1.__next__()) 16 print(next(f1)) 17 print(next(f1)) 18 19 for i in f1: 20 if i > 100: 21 break 22 print('%s ' %i,end='')
十二、__doc__
它類的描述信息1 class Foo: 2 '我是描述信息' 3 pass 4 5 print(Foo.__doc__)
該屬性無法被繼承
1 class Foo: 2 '我是描述信息' 3 pass 4 5 class Bar(Foo): 6 pass 7 8 print(Bar.__doc__) #該屬性無法繼承給子類
十三、__module__和__class__
__module__ 表示當前操作的對象在那個模塊 __class__ 表示當前操作的對象的類是什麼 lib/aa.py1 class C: 2 def __init__(self): 3 self.name = ‘SB'
index.py
1 from lib.aa import C 2 3 obj = C() 4 print obj.__module__ # 輸出 lib.aa,即:輸出模塊 5 print obj.__class__ # 輸出 lib.aa.C,即:輸出類
十四、__del__
析構方法,當對象在記憶體中被釋放時,自動觸發執行。 註:如果產生的對象僅僅只是python程式級別的(用戶級),那麼無需定義__del__,如果產生的對象的同時還會向操作系統發起系統調用,即一個對象有用戶級與內核級兩種資源,比如(打開一個文件,創建一個資料庫鏈接),則必須在清除對象的同時回收系統資源,這就用到了__del__。 簡單示範1 class Foo: 2 def __del__(self): 3 print('執行我啦') 4 5 f1=Foo() 6 del f1 7 print('------->') 8 9 #輸出結果 10 執行我啦 11 ------->
挖坑埋了你
1 class Foo: 2 def __del__(self): 3 print('執行我啦') 4 5 f1=Foo() # del f1 6 print('------->') 7 8 #輸出結果 9 -------> 10 執行我啦
典型的應用場景: 創建資料庫類,用該類實例化出資料庫鏈接對象,對象本身是存放於用戶空間記憶體中,而鏈接則是由操作系統管理的,存放於內核空間記憶體中 當程式結束時,python只會回收自己的記憶體空間,即用戶態記憶體,而操作系統的資源則沒有被回收,這就需要我們定製__del__,在對象被刪除前向操作系統發起關閉資料庫鏈接的系統調用,回收資源 這與文件處理是一個道理:
1 f=open('a.txt') #做了兩件事,在用戶空間拿到一個f變數,在操作系統內核空間打開一個文件 2 del f #只回收用戶空間的f,操作系統的文件還處於打開狀態 3 4 #所以我們應該在del f之前保證f.close()執行,即便是沒有del,程式執行完畢也會自動del清理資源,於是文件操作的正確用法應該是 5 f=open('a.txt') 6 讀寫... 7 f.close() 8 很多情況下大家都容易忽略f.close,這就用到了with上下文管理
十五、__enter__和__exit__
我們知道在操作文件對象的時候可以這麼寫1 with open('a.txt') as f: 2 '代碼塊'
上述叫做上下文管理協議,即with語句,為了讓一個對象相容with語句,必須在這個對象的類中聲明__enter__和__exit__方法
上下文管理協議1 class Open: 2 def __init__(self,name): 3 self.name=name 4 def __enter__(self): 5 print('出現with語句,對象的__enter__被觸發,有返回值則賦值給as聲明的變數') 6 # return self 7 def __exit__(self, exc_type, exc_val, exc_tb): 8 print('with中代碼塊執行完畢時執行我啊') 9 10 11 with Open('a.txt') as f: 12 print('=====>執行代碼塊') 13 # print(f,f.name)
__exit__()中的三個參數分別代表異常類型,異常值和追溯信息,with語句中代碼塊出現異常,則with後的代碼都無法執行
1 class Open: 2 def __init__(self,name): 3 self.name=name 4 5 def __enter__(self): 6 print('出現with語句,對象的__enter__被觸發,有返回值則賦值給as聲明的變數') 7 8 def __exit__(self, exc_type, exc_val, exc_tb): 9 print('with中代碼塊執行完畢時執行我啊') 10 print(exc_type) 11 print(exc_val) 12 print(exc_tb) 13 14 15 with Open('a.txt') as f: 16 print('=====>執行代碼塊') 17 raise AttributeError('***著火啦,救火啊***') 18 print(