面向對象學習目錄 1 面向對象介紹 2 類、實例、屬性、方法詳解 3 面向過程與面向對象進一步比較 4 類與對象 5 屬性查找與綁定方法 6 小結 7 繼承與派生 8 組合 9 抽象類 10 多態 11 封裝 12 綁定方法與非綁定方法 13 內置方法(上) 14 內置方法(中)之描述符 15 內置 ...
面向對象學習目錄
一、isinstance(obj,cls)和issubclass(sub,super)
isinstance(obj,cls)檢查是否obj是否是類 cls 的對象1 class Foo(object): 2 pass 3 4 obj = Foo() 5 isinstance(obj, Foo)
issubclass(sub, super)檢查sub類是否是 super 類的派生類
1 class Foo(object): 2 pass 3 4 class Bar(Foo): 5 pass 6 7 issubclass(Bar, Foo)
二、反射
1 什麼是反射
反射的概念是由Smith在1982年首次提出的,主要是指程式可以訪問、檢測和修改它本身狀態或行為的一種能力(自省)。這一概念的提出很快引發了電腦科學領域關於應用反射性的研究。它首先被程式語言的設計領域所採用,併在Lisp和麵向對象方面取得了成績。2 python面向對象中的反射:通過字元串的形式操作對象相關的屬性。python中的一切事物都是對象(都可以使用反射)
四個可以實現自省的函數——下列方法適用於類和對象(一切皆對象,類本身也是一個對象):hasattr(object,name)
判斷object中有沒有一個name字元串對應的方法或屬性
getattr(object, name, default=None)
def getattr(object, name, default=None): # known special case of getattr """ getattr(object, name[, default]) -> value Get a named attribute from an object; getattr(x, 'y') is equivalent to x.y. When a default argument is given, it is returned when the attribute doesn't exist; without it, an exception is raised in that case. """ pass
setattr(x, y, v)
def setattr(x, y, v): # real signature unknown; restored from __doc__ """ Sets the named attribute on the given object to the specified value. setattr(x, 'y', v) is equivalent to ``x.y = v'' """ pass
delattr(x, y)
def delattr(x, y): # real signature unknown; restored from __doc__ """ Deletes the named attribute from the given object. delattr(x, 'y') is equivalent to ``del x.y'' """ pass
四個方法的使用演示
1 class BlackMedium: 2 feature='Ugly' 3 def __init__(self,name,addr): 4 self.name=name 5 self.addr=addr 6 7 def sell_house(self): 8 print('%s 黑中介賣房子啦,傻逼才買呢,但是誰能證明自己不傻逼' %self.name) 9 10 def rent_house(self): 11 print('%s 黑中介租房子啦,傻逼才租呢' %self.name) 12 13 b1=BlackMedium('萬成置地','回龍觀天露園') 14 15 #檢測是否含有某屬性 16 print(hasattr(b1,'name')) 17 print(hasattr(b1,'sell_house'))
1 #獲取屬性 2 n=getattr(b1,'name') 3 print(n) 4 func=getattr(b1,'rent_house') 5 func() 6 7 # getattr(b1,'aaaaaaaa') #報錯 8 print(getattr(b1,'aaaaaaaa','不存在啊'))
1 #設置屬性 2 setattr(b1,'sb',True) 3 setattr(b1,'show_name',lambda self:self.name+'sb') 4 print(b1.__dict__) 5 print(b1.show_name(b1))
1 #刪除屬性 2 delattr(b1,'addr') 3 delattr(b1,'show_name') 4 delattr(b1,'show_name111') #不存在,則報錯 5 6 print(b1.__dict__)
類也是對象
1 class Foo(object): 2 staticField = "old boy" 3 4 def __init__(self): 5 self.name = 'wupeiqi' 6 7 def func(self): 8 return 'func' 9 10 @staticmethod 11 def bar(): 12 return 'bar' 13 14 print getattr(Foo, 'staticField') 15 print getattr(Foo, 'func') 16 print getattr(Foo, 'bar')
反射當前模塊成員
1 import sys 2 3 def s1(): 4 print 's1' 5 6 def s2(): 7 print 's2' 8 9 this_module = sys.modules[__name__] 10 11 hasattr(this_module, 's1') 12 getattr(this_module, 's2')
導入其他模塊,利用反射查找該模塊是否存在某個方法
1 """ 2 程式目錄內容: 3 module_test.py 4 index.py 5 6 當前文件: 7 index.py 8 """ 9 10 import module_test as obj 11 12 #obj.test() 13 14 print(hasattr(obj,'test')) 15 16 getattr(obj,'test')()
3 為什麼用反射之反射的好處
好處一:實現可插拔機制 有倆程式員,一個lili,一個是egon,lili在寫程式的時候需要用到egon所寫的類,但是egon去跟女朋友度蜜月去了,還沒有完成他寫的類,lili想到了反射,使用了反射機制lili可以繼續完成自己的代碼,等egon度蜜月回來後再繼續完成類的定義並且去實現lili想要的功能。 總之反射的好處就是,可以事先定義好介面,介面只有在被完成後才會真正執行,這實現了即插即用,這其實是一種‘後期綁定’,什麼意思?即你可以事先把主要的邏輯寫好(只定義介面),然後後期再去實現介面的功能 egon還沒有實現全部功能1 class FtpClient: 2 'ftp客戶端,但是還麽有實現具體的功能' 3 def __init__(self,addr): 4 print('正在連接伺服器[%s]' %addr) 5 self.addr=addr不影響lili的代碼編寫
1 #from module import FtpClient 2 f1=FtpClient('192.168.1.1') 3 if hasattr(f1,'get'): 4 func_get=getattr(f1,'get') 5 func_get() 6 else: 7 print('---->不存在此方法') 8 print('處理其他的邏輯')
好處二:動態導入模塊(基於反射當前模塊成員)
三、__setattr__,__delattr__,__getattr__
三者的用法演示1 class Foo: 2 x=1 3 def __init__(self,y): 4 self.y=y 5 6 def __getattr__(self, item): 7 print('----> from getattr:你找的屬性不存在') 8 9 def __setattr__(self, key, value): 10 print('----> from setattr') 11 # self.key=value #這就無限遞歸了,你好好想想 12 # self.__dict__[key]=value #應該使用它 13 14 def __delattr__(self, item): 15 print('----> from delattr') 16 # del self.item #無限遞歸了 17 self.__dict__.pop(item) 18 19 #__setattr__添加/修改屬性會觸發它的執行 20 f1=Foo(10)print(f1.__dict__) # 因為你重寫了__setattr__,凡是賦值操作都會觸發它的運行,你啥都沒寫,就是根本沒賦值,除非你直接操作屬性字典,否則永遠無法賦值 21 f1.z=3print(f1.__dict__) 22 23 #__delattr__刪除屬性的時候會觸發 24 f1.__dict__['a']=3#我們可以直接修改屬性字典,來完成添加/修改屬性的操作del f1.a 25 print(f1.__dict__) 26 27 #__getattr__只有在使用點調用屬性且屬性不存在的時候才會觸發 28 f1.xxxxxx
四、二次加工標準類型(包裝)
包裝:python為大家提供了標準數據類型,以及豐富的內置方法,其實在很多場景下我們都需要基於標準數據類型來定製我們自己的數據類型,新增/改寫方法,這就用到了我們剛學的繼承/派生知識(其他的標準類型均可以通過下麵的方式進行二次加工) 二次加工標準類型(基於繼承實現)1 class List(list): #繼承list所有的屬性,也可以派生出自己新的,比如append和mid 2 def append(self, p_object): 3 ' 派生自己的append:加上類型檢查' 4 if not isinstance(p_object,int): 5 raise TypeError('must be int') 6 super().append(p_object) 7 8 @property 9 def mid(self): 10 '新增自己的屬性' 11 index=len(self)//2 12 return self[index] 13 14 15 l=List([1,2,3,4]) 16 print(l) 17 l.append(5) 18 print(l) # l.append('1111111') #報錯,必須為int類型 19 20 print(l.mid) 21 22 #其餘的方法都繼承list的 23 l.insert(0,-123) 24 print(l) 25 l.clear() 26 print(l)
練習(clear加許可權限制)
1 class List(list): 2 def __init__(self,item,tag=False): 3 super().__init__(item) 4 self.tag=tag 5 def append(self, p_object): 6 if not isinstance(p_object,str): 7 raise TypeError 8 super().append(p_object) 9 def clear(self): 10 if not self.tag: 11 raise PermissionError 12 super().clear() 13 14 l=List([1,2,3],False) 15 print(l) 16 print(l.tag) 17 18 l.append('saf') 19 print(l) 20 21 # l.clear() #異常 22 23 l.tag=True 24 l.clear()授權:授權是包裝的一個特性, 包裝一個類型通常是對已存在的類型的一些定製,這種做法可以新建,修改或刪除原有產品的功能。其它的則保持原樣。授權的過程,即是所有更新的功能都是由新類的某部分來處理,但已存在的功能就授權給對象的預設屬性。 實現授權的關鍵點就是覆蓋__getattr__方法 授權示範一
import time class FileHandle: def __init__(self,filename,mode='r',encoding='utf-8'): self.file=open(filename,mode,encoding=encoding) def write(self,line): t=time.strftime('%Y-%m-%d %T') self.file.write('%s %s' %(t,line)) def __getattr__(self, item): return getattr(self.file,item) f1=FileHandle('b.txt','w+') f1.write('你好啊') f1.seek(0) print(f1.read()) f1.close()
授權示範二
1 #我們來加上b模式支持 2 import time 3 class FileHandle: 4 def __init__(self,filename,mode='r',encoding='utf-8'): 5 if 'b' in mode: 6 self.file=open(filename,mode) 7 else: 8 self.file=open(filename,mode,encoding=encoding) 9 self.filename=filename 10 self.mode=mode 11 self.encoding=encoding 12 13 def write(self,line): 14 if 'b' in self.mode: 15 if not isinstance(line,bytes): 16 raise TypeError('must be bytes') 17 self.file.write(line) 18 19 def __getattr__(self, item): 20 return getattr(self.file,item) 21 22 def __str__(self): 23 if 'b' in self.mode: 24 res="<_io.BufferedReader name='%s'>" %self.filename 25 else: 26 res="<_io.TextIOWrapper name='%s' mode='%s' encoding='%s'>" %(self.filename,self.mode,self.encoding) 27 return res 28 29 f1=FileHandle('b.txt','wb')# f1.write('你好啊啊啊啊啊') 30 #自定製的write,不用在進行encode轉成二進位去寫了,簡單,大氣 31 f1.write('你好啊'.encode('utf-8'))print(f1) 32 f1.close()
練習題(授權)
【練習一】1 class List: 2 def __init__(self,seq): 3 self.seq=seq 4 5 def append(self, p_object): 6 ' 派生自己的append加上類型檢查,覆蓋原有的append' 7 if not isinstance(p_object,int): 8 raise TypeError('must be int') 9 self.seq.append(p_object) 10 11 @property 12 def mid(self): 13 '新增自己的方法' 14 index=len(self.seq)//2 15 return self.seq[index] 16 17 def __getattr__(self, item): 18 return getattr(self.seq,item) 19 20 def __str__(self): 21 return str(self.seq) 22 23 l=List([1,2,3]) 24 print(l) 25 l.append(4) 26 print(l) # l.append('3333333') 27 #報錯,必須為int類型 28 29 print(l.mid) 30 31 #基於授權,獲得insert方法 32 l.insert(0,-123) 33 print(l)
【練習二】
1 class List: 2 def __init__(self,seq,permission=False): 3 self.seq=seq 4 self.permission=permission 5 def clear(self): 6 if not self.permission: 7 raise PermissionError('not allow the operation') 8 self.seq.clear() 9 def __getattr__(self, item): 10 return getattr(self.seq,item) 11 12 def __str__(self): 13 return str(self.seq) 14 15 l=List([1,2,3]) # l.clear() #此時沒有許可權,拋出異常 16 17 l.permission=True 18 print(l) 19 l.clear() 20 print(l) 21 22 #基於授權,獲得insert方法 23 l.insert(0,-123) 24 print(l)
五、__getattribute__
回顧__getattr__1 class Foo: 2 def __init__(self,x): 3 self.x=x 4 5 def __getattr__(self, item): 6 print('執行的是我') 7 # return self.__dict__[item] 8 9 f1=Foo(10) 10 print(f1.x) 11 f1.xxxxxx #不存在的屬性訪問,觸發__getattr__
getattribute
1 class Foo: 2 def __init__(self,x): 3 self.x=x 4 5 def __getattribute__(self, item): 6 print('不管是否存在,我都會執行') 7 8 f1=Foo(10) 9 f1.x 10 f1.xxxxxx二者同時出現
1 class Foo: 2 def __init__(self,x): 3 self.x=x 4 5 def __getattr__(self, item): 6 print('執行的是我') 7 # return self.__dict__[item] 8 9 def __getattribute__(self, item): 10 print('不管是否存在,我都會執行') 11 raise AttributeError('哈哈') 12 13 f1=Foo(10) 14 f1.x 15 f1.xxxxxx 16 17 #當__getattribute__與__getattr__同時存在,只會執行__getattrbute__,除非__getattribute__在執行過程中拋出異常AttributeError