python包裝和授權(quán)學(xué)習(xí)教程
前言
所有便準(zhǔn)數(shù)據(jù)類型都可以通過兩種方式產(chǎn)生,一種是直接定義,一種是使用相應(yīng)的函數(shù),比如要產(chǎn)生字符串,可以s=’hello’或s=str(hello)。像str這種函數(shù)都是工廠函數(shù),實(shí)際上工廠函數(shù)都是類,我們可以通過繼承這些類來定制自己的數(shù)據(jù)類型。(包裝)
包裝
python為大家提供了標(biāo)準(zhǔn)數(shù)據(jù)類型,以及豐富的內(nèi)置方法,其實(shí)在很多場景下我們都需要基于標(biāo)準(zhǔn)數(shù)據(jù)類型來定制我們自己的數(shù)據(jù)類型,新增/改寫方法,這就用到了繼承/派生知識(其他的標(biāo)準(zhǔn)類型均可以通過下面的方式進(jìn)行二次加工)。
二次加工標(biāo)準(zhǔn)類型(基于繼承實(shí)現(xiàn))
class List(list): #繼承l(wèi)ist所有的屬性,也可以派生出自己新的,比如append和mid def append(self, p_object): ' 派生自己的append:加上類型檢查' if not isinstance(p_object,int): #if type(p_object) is int: raise TypeError('must be int') #self.append(p_object) #死循環(huán) #list.append(self, p_object) #調(diào)用父類的方法 super().append(p_object) #調(diào)用父類的方法 @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') #報(bào)錯(cuò),必須為int類型 print(l.mid) #其余的方法都繼承l(wèi)ist的 l.insert(0,-123) print(l) l.clear() print(l)
clear加權(quán)限限制
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()
授權(quán)
授權(quán)是包裝的一個(gè)特性, 包裝一個(gè)類型通常是對已存在的類型的一些定制,這種做法可以新建,修改或刪除原有產(chǎn)品的功能。其它的則保持原樣。授權(quán)的過程,即是所有更新的功能都是由新類的某部分來處理,但已存在的功能就授權(quán)給對象的默認(rèn)屬性。
授權(quán)是采用已存在的功能達(dá)到最大限度的代碼重用。在包裝中我們可以新建、修改或刪除已有的功能,授權(quán)的過程就是將更新的功能交由新類來處理,已存在的功能就授權(quán)給對象的默認(rèn)屬性。
實(shí)現(xiàn)授權(quán)的關(guān)鍵點(diǎn)就是覆蓋__getattr__方法 (包裝是通過繼承實(shí)現(xiàn))。在代碼里包含一個(gè)對getattr()內(nèi)建函數(shù)的調(diào)用,調(diào)用getattr()得到默認(rèn)對象的屬性(數(shù)據(jù)屬性或者方法)并返回它,以便于訪問或者調(diào)用。
當(dāng)引用一個(gè)屬性時(shí),解釋器首先會在局部名稱空間中查找那個(gè)名字,比如一個(gè)自定義的方法或局部實(shí)例屬性。如果沒有在局部字典中找到,則搜索類名稱空間,以防一個(gè)類屬性被訪問。最后,如果兩類搜索都失敗了,搜索則對原對象開始授權(quán)請求,此時(shí)__getattr__()會被調(diào)用。
示例1
import time class FileHandle: def __init__(self,filename,mode='r',encoding='utf-8'): self.file=open(filename,mode,encoding=encoding) def write(self,line): #定制自己的write方法 – 對寫入的內(nèi)容加上時(shí)間 t=time.strftime('%Y-%m-%d %T') self.file.write('%s %s' %(t,line)) def __getattr__(self, item): return getattr(self.file,item)# self.file是通過系統(tǒng)默認(rèn)的open獲取的,它包含了默認(rèn)open的所有方法,item是一個(gè)字符串,getattr以字符串形式調(diào)用方法 f1=FileHandle('b.txt','w+') f1.write('你好啊') f1.seek(0) print(f1.read()) f1.close()
在實(shí)例和類FileHandle中都沒有找到該屬性,所以觸發(fā)__getattr__先找f1的屬性字典,沒有則找類FileHandle的屬性字典,沒有則觸發(fā)__getattr__,__getattr__中調(diào)用的是系統(tǒng)open返回的文件描述符的方法,通過自己的類實(shí)例化,調(diào)用系統(tǒng)的方法。
示例2
#_*_coding:utf-8_*_ __author__ = 'Linhaifeng' #我們來加上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,不用在進(jìn)行encode轉(zhuǎn)成二進(jìn)制去寫了 f1.write('你好啊'.encode('utf-8')) print(f1) f1.close()
示例3
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') #報(bào)錯(cuò),必須為int類型 print(l.mid) #基于授權(quán),獲得insert方法 l.insert(0,-123) print(l)
示例4
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() #此時(shí)沒有權(quán)限,拋出異常 l.permission=True print(l) l.clear() print(l) #基于授權(quán),獲得insert方法 l.insert(0,-123) print(l)
__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 #這就無限遞歸了,因?yàn)樵O(shè)置屬性會觸發(fā)__setattr__ # self.__dict__[key]=value #應(yīng)該使用它,直接修改底層字典 def __delattr__(self, item): print('----> from delattr') # del self.item #無限遞歸了 self.__dict__.pop(item)# 直接在底層字典刪除,就不會觸發(fā)了 #__setattr__添加/修改屬性會觸發(fā)它的執(zhí)行 f1=Foo(10) print(f1.__dict__) # 因?yàn)槟阒貙懥薩_setattr__,凡是賦值操作都會觸發(fā)它的運(yùn)行,你啥都沒寫,就是根本沒賦值,除非你直接操作屬性字典,否則永遠(yuǎn)無法賦值 f1.z=3 print(f1.__dict__) #__delattr__刪除屬性的時(shí)候會觸發(fā) f1.__dict__['a']=3#我們可以直接修改屬性字典,來完成添加/修改屬性的操作 del f1.a print(f1.__dict__) #__getattr__只有在使用點(diǎn)調(diào)用屬性且屬性不存在的時(shí)候才會觸發(fā) f1.xxxxxx
getattr(obj)相當(dāng)于obj.__getattr__()
setattr(obj)相當(dāng)于obj.__setattr__()
delattr(obj)相當(dāng)于obj.__delattr__()
- 類的內(nèi)置attr屬性,你不定義的話會用默認(rèn)的,如果定義了則用自己定義的。
- class.at 調(diào)用時(shí)觸發(fā):如果類class中屬性at不存在,則觸發(fā)__getattr__
- del class.at刪除時(shí)觸發(fā):刪除類class的屬性at時(shí),觸發(fā)__delattr__
- class.at=1設(shè)置時(shí)觸發(fā):設(shè)置類class的屬性at時(shí),觸發(fā)__setattr__
自己定義這三個(gè)函數(shù),在相應(yīng)操作時(shí),就可以觸發(fā)自己想要的邏輯。
class MyClass: def __init__(self, name) self.name = name #觸發(fā)__setattr__ def __getattr__(self, item) #默認(rèn)的__getattr__是屬性不存在則報(bào)錯(cuò),我們修改后不報(bào)錯(cuò),僅打印提示 print(“attr [%s] not found” %item) def __setattr__(self, k, v) #默認(rèn)的setattr會把設(shè)置的屬性加在屬性字典,但是我們自定義的setattr什么也沒做,屬性字典不會增加屬性 print(‘set attr', k, v) if tyoe(v) is str: #限制value只能以字符串的形式設(shè)置 print(‘set') #self.k = v #再次觸發(fā)__setattr__,進(jìn)入死循環(huán) self.__dict__[k] = v.upper() #真正的設(shè)置 – 直接操作底層數(shù)據(jù)結(jié)構(gòu),在屬性字典中添加 else: print(‘not str') def __delattr__(self, item) """ #自己控制-所有屬性不可刪除 print(‘can not delete [%s]' %item) pass """ print(‘delete attr[%s]' %item) #del self.item #觸發(fā)__delattr__ 進(jìn)入死循環(huán) self.__dict__.pop(item) #真正的刪除 mc = MyClass(‘hello') #實(shí)例化的時(shí)候會觸發(fā)__init__ print(mc.name) #hello print(mc.hhh) #觸發(fā)__getattr__ mc?self hhh?item(字符串的形式) mc.age = 18 mc.age = ‘18' #觸發(fā)__setattr__ mc?self age?k ‘18'?v del mc.name #觸發(fā)__delattr__ mc?self name? item(字符串的形式)
__getattribute__
#__getattr__ class Foo: def __init__(self,x): self.x=x def __getattr__(self, item): print('執(zhí)行的是我') # return self.__dict__[item] f1=Foo(10) print(f1.x) f1.xxxxxx #不存在的屬性訪問,觸發(fā)__getattr__
#__getattribute__ - 不管屬性是否存在,都會觸發(fā) class Foo: def __init__(self,x): self.x=x def __getattribute__(self, item): print('不管是否存在,我都會執(zhí)行') f1=Foo(10) f1.x f1.xxxxxx
如果二者同時(shí)出現(xiàn)
#_*_coding:utf-8_*_ __author__ = 'Linhaifeng' class Foo: def __init__(self,x): self.x=x def __getattr__(self, item): print('執(zhí)行的是我') # return self.__dict__[item] def __getattribute__(self, item): print('不管是否存在,我都會執(zhí)行') raise AttributeError('哈哈') #拋出AttributeError異常–跳轉(zhuǎn)到__getattr__ f1=Foo(10) f1.x f1.xxxxxx
當(dāng)__getattribute__與__getattr__同時(shí)存在,只會執(zhí)行__getattribute__,除非__getattribute__在執(zhí)行過程中拋出異常AttributeError,系統(tǒng)默認(rèn)的__getattribute__,如果查找的屬性不存在,當(dāng)拋出異常AttributeError的時(shí)候,會跳轉(zhuǎn)到__getattr__,也就是說,只有拋出異常AttributeError的時(shí)候,才會執(zhí)行__getattr__,其他情況下只會執(zhí)行__getattribute__。系統(tǒng)提供的默認(rèn)__getattribute__,當(dāng)要查找的屬性存在時(shí),會在當(dāng)前屬性字典中返回屬性,如果要找的屬性不存在會拋出異常AttributeError,轉(zhuǎn)到__getattr__去執(zhí)行,__getattr__接收異常來避免程序崩潰,并執(zhí)行定義好的邏輯。
總結(jié)
到此這篇關(guān)于python包裝和授權(quán)的文章就介紹到這了,更多相關(guān)python包裝和授權(quán)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
一步步教你用python給女朋友寫個(gè)微信自動(dòng)提醒的程序
如今微信已成為我們?nèi)粘I畹闹饕涣鞴ぞ?但是微信自身的功能有時(shí)候可能并不能滿足我們的需要,因此我們會想是否可以進(jìn)行微信功能的拓展呢,這篇文章主要給大家介紹了關(guān)于利用python給女朋友寫了個(gè)微信自動(dòng)提醒程序的相關(guān)資料,需要的朋友可以參考下2021-10-10CentOS下Python3的安裝及創(chuàng)建虛擬環(huán)境的方法
這篇文章主要介紹了CentOS下Python3的安裝及創(chuàng)建虛擬環(huán)境的方法,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2018-11-11最新版 Windows10上安裝Python 3.8.5的步驟詳解
這篇文章主要介紹了最新版 Windows10上安裝Python 3.8.5的步驟詳解,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-11-11