python學習------面向物件進階
一 isinstance(obj,cls)和issubclass(sub,super)
isinstance(obj,cls)檢查是否obj是否是類 cls 的物件
class Foo(object): pass obj = Foo() isinstance(obj, Foo)
issubclass(sub, super)檢查sub類是否是 super 類的派生類
class Foo(object): pass class Bar(Foo): pass issubclass(Bar, Foo)
二 反射
1 什麼是反射
反射的概念是由Smith在1982年首次提出的,主要是指程式可以訪問、檢測和修改它本身狀態或行為的一種能力(自省)。這一概念的提出很快引發了電腦科學領域關於應用反射性的研究。它首先被程式語言的設計領域所採用,並在Lisp和麵向物件方面取得了成績。
2 python面向物件中的反射:通過字串的形式操作物件相關的屬性。python中的一切事物都是物件(都可以使用反射)
四個可以實現自省的函式
下列方法適用於類和物件(一切皆物件,類本身也是一個物件)
#hasattr(object,name) #判斷object中有沒有一個name字串對應的方法或屬性hasattr(object,name)
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.getattr(object, name, default=None)""" pass getattr(object, name, default=None)
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 setattr(x, y, v)setattr(x, y, v)
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 delattr(x, y)delattr(x, y)
class BlackMedium: feature='Ugly' def __init__(self,name,addr): self.name=name self.addr=addr def sell_house(self): print('%s 黑中介賣房子啦,傻逼才買呢,但是誰能證明自己不傻逼' %self.name) def rent_house(self): print('%s 黑中介租房子啦,傻逼才租呢' %self.name) b1=BlackMedium('萬成置地','回龍觀天露園') #檢測是否含有某屬性 print(hasattr(b1,'name')) print(hasattr(b1,'sell_house')) #獲取屬性 n=getattr(b1,'name') print(n) func=getattr(b1,'rent_house') func() # getattr(b1,'aaaaaaaa') #報錯 print(getattr(b1,'aaaaaaaa','不存在啊')) #設定屬性 setattr(b1,'sb',True) setattr(b1,'show_name',lambda self:self.name+'sb') print(b1.__dict__) print(b1.show_name(b1)) #刪除屬性 delattr(b1,'addr') delattr(b1,'show_name') delattr(b1,'show_name111')#不存在,則報錯 print(b1.__dict__)四個方法的使用演示
class Foo(object): staticField = "old boy" def __init__(self): self.name = 'wupeiqi' def func(self): return 'func' @staticmethod def bar(): return 'bar' print getattr(Foo, 'staticField') print getattr(Foo, 'func') print getattr(Foo, 'bar')類也是物件
#!/usr/bin/env python # -*- coding:utf-8 -*- import sys def s1(): print 's1' def s2(): print 's2' this_module = sys.modules[__name__] hasattr(this_module, 's1') getattr(this_module, 's2') 反射當前模組成員反射當前模組成員
匯入其他模組,利用反射查詢該模組是否存在某個方法
#!/usr/bin/env python # -*- coding:utf-8 -*- """ 程式目錄: module_test.py index.py 當前檔案: index.py """ import module_test as obj #obj.test() print(hasattr(obj,'test')) getattr(obj,'test')()module_test.py
3 為什麼用反射之反射的好處
好處一:實現可插拔機制
有倆程式設計師,一個lili,一個是egon,lili在寫程式的時候需要用到egon所寫的類,但是egon去跟女朋友度蜜月去了,還沒有完成他寫的類,lili想到了反射,使用了反射機制lili可以繼續完成自己的程式碼,等egon度蜜月回來後再繼續完成類的定義並且去實現lili想要的功能。
總之反射的好處就是,可以事先定義好介面,介面只有在被完成後才會真正執行,這實現了即插即用,這其實是一種‘後期繫結’,什麼意思?即你可以事先把主要的邏輯寫好(只定義介面),然後後期再去實現介面的功能
class FtpClient: 'ftp客戶端,但是還麼有實現具體的功能' def __init__(self,addr): print('正在連線伺服器[%s]' %addr) self.addr=addregon還沒有實現全部功能
#from module import FtpClient f1=FtpClient('192.168.1.1') if hasattr(f1,'get'): func_get=getattr(f1,'get') func_get() else: print('---->不存在此方法') print('處理其他的邏輯') 不影響lili的程式碼編寫不影響lili的程式碼編寫
好處二:動態匯入模組(基於反射當前模組成員)
三 __setattr__,__delattr__,__getattr__
class Foo: x=1 def __init__(self,y): self.y=y def __getattr__(self, item): print('----> from getattr:你找的屬性不存在') def __setattr__(self, key, value): print('----> from setattr') # self.key=value #這就無限遞迴了,你好好想想 # self.__dict__[key]=value #應該使用它 def __delattr__(self, item): print('----> from delattr') # del self.item #無限遞迴了 self.__dict__.pop(item) #__setattr__新增/修改屬性會觸發它的執行 f1=Foo(10) print(f1.__dict__) # 因為你重寫了__setattr__,凡是賦值操作都會觸發它的執行,你啥都沒寫,就是根本沒賦值,除非你直接操作屬性字典,否則永遠無法賦值 f1.z=3 print(f1.__dict__) #__delattr__刪除屬性的時候會觸發 f1.__dict__['a']=3#我們可以直接修改屬性字典,來完成新增/修改屬性的操作 del f1.a print(f1.__dict__) #__getattr__只有在使用點呼叫屬性且屬性不存在的時候才會觸發 f1.xxxxxx三者的用法演示
四 二次加工標準型別(包裝)
包裝:python為大家提供了標準資料型別,以及豐富的內建方法,其實在很多場景下我們都需要基於標準資料型別來定製我們自己的資料型別,新增/改寫方法,這就用到了我們剛學的繼承/派生知識(其他的標準型別均可以通過下面的方式進行二次加工)
class List(list): #繼承list所有的屬性,也可以派生出自己新的,比如append和mid def append(self, p_object): ' 派生自己的append:加上型別檢查' if not isinstance(p_object,int): raise TypeError('must be int') super().append(p_object) @property def mid(self): '新增自己的屬性' index=len(self)//2 return self[index] l=List([1,2,3,4]) print(l) l.append(5) print(l) # l.append('1111111') #報錯,必須為int型別 print(l.mid) #其餘的方法都繼承list的 l.insert(0,-123) print(l) l.clear() print(l)二次加工標準型別(基於繼承實現)
class List(list): def __init__(self,item,tag=False): super().__init__(item) self.tag=tag def append(self, p_object): if not isinstance(p_object,str): raise TypeError super().append(p_object) def clear(self): if not self.tag: raise PermissionError super().clear() l=List([1,2,3],False) print(l) print(l.tag) l.append('saf') print(l) # l.clear() #異常 l.tag=True l.clear()練習(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() 授權示範一授權示範一
#_*_coding:utf-8_*_ #我們來加上b模式支援 import time class FileHandle: def __init__(self,filename,mode='r',encoding='utf-8'): if 'b' in mode: self.file=open(filename,mode) else: self.file=open(filename,mode,encoding=encoding) self.filename=filename self.mode=mode self.encoding=encoding def write(self,line): if 'b' in self.mode: if not isinstance(line,bytes): raise TypeError('must be bytes') self.file.write(line) def __getattr__(self, item): return getattr(self.file,item) def __str__(self): if 'b' in self.mode: res="<_io.BufferedReader name='%s'>" %self.filename else: res="<_io.TextIOWrapper name='%s' mode='%s' encoding='%s'>" %(self.filename,self.mode,self.encoding) return res f1=FileHandle('b.txt','wb') # f1.write('你好啊啊啊啊啊') #自定製的write,不用在進行encode轉成二進位制去寫了,簡單,大氣 f1.write('你好啊'.encode('utf-8')) print(f1) f1.close()授權示範二
#練習一 class List: def __init__(self,seq): self.seq=seq def append(self, p_object): ' 派生自己的append加上型別檢查,覆蓋原有的append' if not isinstance(p_object,int): raise TypeError('must be int') self.seq.append(p_object) @property def mid(self): '新增自己的方法' index=len(self.seq)//2 return self.seq[index] def __getattr__(self, item): return getattr(self.seq,item) def __str__(self): return str(self.seq) l=List([1,2,3]) print(l) l.append(4) print(l) # l.append('3333333') #報錯,必須為int型別 print(l.mid) #基於授權,獲得insert方法 l.insert(0,-123) print(l) #練習二 class List: def __init__(self,seq,permission=False): self.seq=seq self.permission=permission def clear(self): if not self.permission: raise PermissionError('not allow the operation') self.seq.clear() def __getattr__(self, item): return getattr(self.seq,item) def __str__(self): return str(self.seq) l=List([1,2,3]) # l.clear() #此時沒有許可權,丟擲異常 l.permission=True print(l) l.clear() print(l) #基於授權,獲得insert方法 l.insert(0,-123) print(l)練習題(授權)
五 __getattribute__
class Foo: def __init__(self,x): self.x=x def __getattr__(self, item): print('執行的是我') # return self.__dict__[item] f1=Foo(10) print(f1.x) f1.xxxxxx #不存在的屬性訪問,觸發__getattr__回顧__getattr__
class Foo: def __init__(self,x): self.x=x def __getattribute__(self, item): print('不管是否存在,我都會執行') f1=Foo(10) f1.x f1.xxxxxx__getattribute__
#_*_coding:utf-8_*_ class Foo: def __init__(self,x): self.x=x def __getattr__(self, item): print('執行的是我') # return self.__dict__[item] def __getattribute__(self, item): print('不管是否存在,我都會執行') raise AttributeError('哈哈') f1=Foo(10) f1.x f1.xxxxxx #當__getattribute__與__getattr__同時存在,只會執行__getattrbute__,除非__getattribute__在執行過程中丟擲異常AttributeError二者同時出現