一、isinstance(obj,cls)檢查是否obj是不是類 cls 的對象python
1 class Foo(object): 2 pass 3 4 obj = Foo() 5 6 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)
一、反射定義:反射的概念是由Smith在1982年首次提出的,主要是指程序能夠訪問、檢測和修改它自己狀態或行爲的一種能力(自省)。這一律唸的提出很快引起了計算機科學領域關於應用反射性的研究。它首先被程序語言的設計領域所採用,並在Lisp和麪向對象方面取得了成績。sql
二、python面向對象中的反射:經過字符串的形式操做對象相關的屬性。python中的一切事物都是對象(均可以使用反射)數據庫
四個能夠實現自省的函數:編程
hasatter(object, name) : 判斷object中有沒有一個name字符串對應的方法或屬性,檢查成員數組
getattr(object, name, default=None): 獲取成員緩存
setattr(x, y, v):設置成員服務器
delattr(x, y):刪除成員網絡
1 class Foo(object): 2 3 def __init__(self): 4 self.name = 'abc' 5 6 def func(self): 7 return 'ok' 8 9 obj = Foo() 10 #獲取成員 11 ret = getattr(obj, 'func')#獲取的是個對象 12 r = ret() 13 print(r) 14 #檢查成員 15 ret = hasattr(obj,'func')#由於有func方法因此返回True 16 print(ret) 17 #設置成員 18 print(obj.name) #設置以前爲:abc 19 ret = setattr(obj,'name',19) 20 print(obj.name) #設置以後爲:19 21 #刪除成員 22 print(obj.name) #abc 23 delattr(obj,'name') 24 print(obj.name) #報錯
a.應用實例(一):使用格式數據結構
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 def rent_house(self): 10 print('%s 黑中介租房子啦,傻逼才租呢' %self.name) 11 12 b1=BlackMedium('萬成置地','回龍觀天露園') 13 14 #檢測是否含有某屬性 15 print(hasattr(b1,'name')) 16 print(hasattr(b1,'sell_house')) 17 18 #獲取屬性 19 n=getattr(b1,'name') 20 print(n) 21 func=getattr(b1,'rent_house') 22 func() 23 24 # getattr(b1,'aaaaaaaa') #報錯 25 print(getattr(b1,'aaaaaaaa','不存在啊')) 26 27 #設置屬性 28 setattr(b1,'sb',True) 29 setattr(b1,'show_name',lambda self:self.name+'sb') 30 print(b1.__dict__) 31 print(b1.show_name(b1)) 32 33 #刪除屬性 34 delattr(b1,'addr') 35 delattr(b1,'show_name') 36 delattr(b1,'show_name111')#不存在,則報錯 37 38 print(b1.__dict__)
b.應用實例(二):類也是對象,便可以反射
1 class Foo(object): 2 3 staticField = "old boy" 4 5 def __init__(self): 6 self.name = 'wupeiqi' 7 8 def func(self): 9 return 'func' 10 11 @staticmethod 12 def bar(): 13 return 'bar' 14 15 print getattr(Foo, 'staticField') 16 print getattr(Foo, 'func') 17 print getattr(Foo, 'bar')
c.應用實例(三):反射當前模塊成員
1 import sys 2 3 4 def s1(): 5 print 's1' 6 7 8 def s2(): 9 print 's2' 10 11 12 this_module = sys.modules[__name__] 13 14 hasattr(this_module, 's1') 15 getattr(this_module, 's2')
d.應用實例(四):導入其餘模塊,利用反射查找該模塊是否存在某個方法
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 4 def test(): 5 print('from the test')
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 4 """ 5 程序目錄: 6 module_test.py 7 index.py 8 9 當前文件: 10 index.py 11 """ 12 13 import module_test as obj 14 15 #obj.test() 16 17 print(hasattr(obj,'test')) 18 19 getattr(obj,'test')()
三、反射的優勢
a.實現可插拔機制.
有倆程序員,一個lili,一個是egon,lili在寫程序的時候須要用到egon所寫的類,可是egon去跟女友度蜜月去了,尚未完成他寫的類,lili想到了反射,使用了反射機制lili能夠繼續完成本身的代碼,等egon度蜜月回來後再繼續完成類的定義而且去實現lili想要的功能。
總之反射的好處就是,能夠事先定義好接口,接口只有在被完成後纔會真正執行,這實現了即插即用,這實際上是一種‘後期綁定’,什麼意思?即你能夠事先把主要的邏輯寫好(只定義接口),而後後期再去實現接口的功能
1 class FtpClient: 2 'ftp客戶端,可是還麼有實現具體的功能' 3 def __init__(self,addr): 4 print('正在鏈接服務器[%s]' %addr) 5 self.addr=addr
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('處理其餘的邏輯')
b.動態導入模塊(基於反射當前模塊成員).
1 ''' 2 動態導入模塊 3 路徑 4 |-import_lib 5 --|-metaclass.py 6 ''' 7 import importlib # 導入importlib內置庫 8 9 importlib.import_module("import_lib.metaclass") #獲得上述路徑文件
類的屬性操做,__dict__的重寫方法。可修改
一、__setattr__,__delattr__,__getattr__(等同於setattr,delattr,getattr),它的用法以下:
__setattr__ 添加/修改屬性會觸發執行
__delattr__ 刪除屬性的時候會觸發
__getlattr__ 只有在使用點調用屬性且屬性不存在的時候纔會觸發。(新式類獨有)
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 10 def __setattr__(self, key, value): 11 print('----> from setattr') 12 # self.key=value #這就無限遞歸了,你好好想一想 13 # self.__dict__[key]=value #應該使用它 14 15 def __delattr__(self, item): 16 print('----> from delattr') 17 # del self.item #無限遞歸了 18 self.__dict__.pop(item) 19 20 #__setattr__添加/修改屬性會觸發它的執行 21 f1=Foo(10) 22 print(f1.__dict__) # 由於你重寫了__setattr__,凡是賦值操做都會觸發它的運行,你啥都沒寫,就是根本沒賦值,除非你直接操做屬性字典,不然永遠沒法賦值 23 f1.z=3 24 print(f1.__dict__) 25 26 #__delattr__刪除屬性的時候會觸發 27 f1.__dict__['a']=3#咱們能夠直接修改屬性字典,來完成添加/修改屬性的操做 28 del f1.a 29 print(f1.__dict__) 30 31 #__getattr__只有在使用點調用屬性且屬性不存在的時候纔會觸發 32 f1.xxxxxx
一、包裝:
python爲你們提供了標準數據類型,以及豐富的內置方法,其實在不少場景下咱們都須要基於標準數據類型來定製咱們本身的數據類型,新增/改寫方法,這就用到了咱們剛學的繼承/派生知識(其餘的標準類型都可以經過下面的方式進行二次加工)如:
1 # List繼承列表類,他繼承了python解釋器list列表的全部功能 2 # class List(list): 3 # pass 4 # 5 # l=List([1,2,3]) 6 # print(l) 7 # l.append(4) 8 # print(l)
經過繼承,可修改添加爲:
1 class List(list): 2 def __init__(self,item,tag=False): 3 super().__init__(item) 4 self.tag=tag 5 # 重寫列表類的append方法,只容許添加str類型,否則報錯 6 def append(self, p_object): 7 # print(p_object) 8 if not isinstance(p_object,str): 9 raise TypeError('%s must be str' %p_object) 10 super(List,self).append(p_object) 11 12 # 添加獲取中間值方法,@property可當聲明靜態變量調用 13 @property 14 def mid(self): 15 mid_index=len(self)//2 16 return self[mid_index] 17 18 # 必須修改tag,才能清空列表 19 def clear(self): 20 if not self.tag: 21 raise PermissionError('not permissive') 22 super().clear() 23 self.tag=False 24 25 # l=List([1,2,3]) 26 # l.append(4) 27 # l.append('aaaaa') 28 # l.append('aaaaa') 29 # print(l) 30 31 # print(l.mid) 32 # l=[1,2,3,4,5,56,6,7,7] 33 # 34 # mid_index=len(l)//2 35 # print(l[mid_index]) 36 37 # l.insert(0,123123123123123) 38 # print(l) 39 40 # l.tag=True 41 # l.clear() 42 # print(l)
二、受權:
受權是包裝的一個特性, 包裝一個類型一般是對已存在的類型的一些定製,這種作法能夠新建,修改或刪除原有產品的功能。其它的則保持原樣。受權的過程,便是全部更新的功能都是由新類的某部分來處理,但已存在的功能就受權給對象的默認屬性。
實現受權的關鍵點就是覆蓋__getattr__方法。主要針對,python標準庫中,不是類屬性的工具模塊如:函數屬性,修改。
範例爲:open方法
1 import time 2 class Open: 3 def __init__(self,filepath,mode='r',encoding='utf-8'): 4 self.filepath=filepath 5 self.mode=mode 6 self.encoding=encoding 7 self.f=open(self.filepath,mode=self.mode,encoding=self.encoding) 8 9 #寫入每行增長日期 10 def write(self,msg): 11 t=time.strftime('%Y-%m-%d %X') 12 self.f.write('%s %s\n' %(t,msg)) 13 14 #其餘除了write的方法,都反射會原來工具處理 15 def __getattr__(self, item): 16 # print(item,type(item)) 17 return getattr(self.f,item) 18 obj=Open('a.txt','w+',encoding='utf-8')
範例二:open方法
1 # 咱們來加上b模式支持 2 class FileHandle: 3 def __init__(self,filename,mode='r',encoding='utf-8'): 4 if 'b' in mode: 5 self.file=open(filename,mode) 6 else: 7 self.file=open(filename,mode,encoding=encoding) 8 self.filename=filename 9 self.mode=mode 10 self.encoding=encoding 11 12 # 咱們來加上b(二進制)模式支持 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 f1=FileHandle('b.txt','wb') 29 # f1.write('你好啊啊啊啊啊') #自定製的write,不用在進行encode轉成二進制去寫了,簡單,大氣 30 f1.write('你好啊'.encode('utf-8')) 31 print(f1) 32 f1.close()
練習:使用受權重寫list的方法
1 #練習一 2 class List: 3 def __init__(self,seq): 4 self.seq=seq 5 6 def append(self, p_object): 7 ' 派生本身的append加上類型檢查,覆蓋原有的append' 8 if not isinstance(p_object,int): 9 raise TypeError('must be int') 10 self.seq.append(p_object) 11 12 @property 13 def mid(self): 14 '新增本身的方法' 15 index=len(self.seq)//2 16 return self.seq[index] 17 18 def __getattr__(self, item): 19 return getattr(self.seq,item) 20 21 def __str__(self): 22 return str(self.seq) 23 24 l=List([1,2,3]) 25 print(l) 26 l.append(4) 27 print(l) 28 # l.append('3333333') #報錯,必須爲int類型 29 30 print(l.mid) 31 32 #基於受權,得到insert方法 33 l.insert(0,-123) 34 print(l) 35 36 37 38 39 40 #練習二 41 class List: 42 def __init__(self,seq,permission=False): 43 self.seq=seq 44 self.permission=permission 45 def clear(self): 46 if not self.permission: 47 raise PermissionError('not allow the operation') 48 self.seq.clear() 49 50 def __getattr__(self, item): 51 return getattr(self.seq,item) 52 53 def __str__(self): 54 return str(self.seq) 55 l=List([1,2,3]) 56 # l.clear() #此時沒有權限,拋出異常 57 58 59 l.permission=True 60 print(l) 61 l.clear() 62 print(l) 63 64 #基於受權,得到insert方法 65 l.insert(0,-123) 66 print(l)
__getattriute__與__getattr_意思相反,無論只要使用點調用屬性無論存不存在,都會執行。且當__getattribute__與__getattr__同時存在,只會執行__getattrbute__,除非__getattribute__在執行過程當中拋出異常AttributeError。
1 #_*_coding:utf-8_*_ 2 __author__ = 'Linhaifeng' 3 4 class Foo: 5 def __init__(self,x): 6 self.x=x 7 8 def __getattr__(self, item): 9 print('執行的是我') 10 # return self.__dict__[item] 11 def __getattribute__(self, item): 12 print('無論是否存在,我都會執行') 13 raise AttributeError('哈哈') 14 15 f1=Foo(10) 16 f1.x 17 f1.xxxxxx
一、描述符
描述符本質就是一個新式類,在這個新式類中,至少實現了__get__(),__set__(),__delete__()中的一個,這也被稱爲描述符協議
__get__():調用一個屬性時,觸發
__set__():爲一個屬性賦值時,觸發
__delete__():採用del刪除屬性時,觸發
定義一個描述符
1 class Foo: #在python3中Foo是新式類,它實現了三種方法,這個類就被稱做一個描述符 2 def __get__(self, instance, owner): 3 pass 4 def __set__(self, instance, value): 5 pass 6 def __delete__(self, instance): 7 pass
二、描述符的做用
描述符的做用是用來代理另一個類的屬性的(必須把描述符定義成這個類的類屬性,不能定義到構造函數中)
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 # Author:Shu Yang Wang 4 # Date: 2017/6/19 5 6 7 #描述符Str 8 class Str: 9 def __get__(self, instance, owner): 10 print('Str調用') 11 def __set__(self, instance, value): 12 print('Str設置...') 13 def __delete__(self, instance): 14 print('Str刪除...') 15 16 #描述符Int 17 class Int: 18 def __get__(self, instance, owner): 19 print('Int調用') 20 def __set__(self, instance, value): 21 print('Int設置...') 22 def __delete__(self, instance): 23 print('Int刪除...') 24 25 class People: 26 name=Str() 27 age=Int() 28 def __init__(self,name,age): #name被Str類代理,age被Int類代理, 29 self.name=name 30 self.age=age 31 32 #何地?:定義成另一個類的類屬性 33 34 #什麼時候?:且看下列演示 35 36 p1=People('alex',18) 37 ''' 38 Str設置... 39 Int設置... 40 ''' 41 42 #描述符Str的使用 43 p1.name 44 p1.name='egon' 45 del p1.name 46 ''' 47 Str調用 48 Str設置... 49 Str刪除... 50 ''' 51 52 #描述符Int的使用 53 p1.age 54 p1.age=18 55 del p1.age 56 ''' 57 Int調用 58 Int設置... 59 Int刪除... 60 ''' 61 62 #咱們來瞅瞅到底發生了什麼 63 print(p1.__dict__) 64 print(People.__dict__) 65 ''' 66 {} 67 {'__module__': '__main__', 'name': <__main__.Str object at 0x0000009B9AB899E8>, 'age': <__main__.Int object at 0x0000009B9AB89A20>, '__init__': <function People.__init__ at 0x0000009B9AB8BBF8>, '__dict__': <attribute '__dict__' of 'People' objects>, '__weakref__': <attribute '__weakref__' of 'People' objects>, '__doc__': None} 68 ''' 69 70 #補充 71 print(type(p1) == People) #type(obj)實際上是查看obj是由哪一個類實例化來的 72 print(type(p1).__dict__ == People.__dict__) 73 ''' 74 True 75 True 76 '''
三、描述符的分類:
a.數據描述符: 至少實現一個__get__() 、__set__()
1 class Foo: 2 def __set__(self, instance, value): 3 print('set') 4 def __get__(self, instance, owner): 5 print('get')
b.非數據描述符
1 class Foo: 2 def __get__(self, instance, owner): 3 print('get')
四、使用注意
a.描述符自己應該定義成新式類,被代理的類也應該是新式類
b.必須把描述符定義成這個類的類屬性,不能爲定義到構造函數中
c.要嚴格遵循該優先級,優先級由高到底分別:類屬性--〉數據描述符--〉實例屬性--〉非數據描述符--〉找不到的屬性觸發__getattr__()
1)類屬性--〉數據描述符
1 #描述符Str 2 class Str: 3 def __get__(self, instance, owner): 4 print('Str調用') 5 def __set__(self, instance, value): 6 print('Str設置...') 7 def __delete__(self, instance): 8 print('Str刪除...') 9 10 class People: 11 name=Str() 12 def __init__(self,name,age): #name被Str類代理,age被Int類代理, 13 self.name=name 14 self.age=age 15 16 17 #基於上面的演示,咱們已經知道,在一個類中定義描述符它就是一個類屬性,存在於類的屬性字典中,而不是實例的屬性字典 18 19 #那既然描述符被定義成了一個類屬性,直接經過類名也必定能夠調用吧,沒錯 20 People.name #恩,調用類屬性name,本質就是在調用描述符Str,觸發了__get__() 21 22 People.name='egon' #那賦值呢,我去,並無觸發__set__() 23 del People.name #趕忙試試del,我去,也沒有觸發__delete__() 24 #結論:描述符對類沒有做用-------->傻逼到家的結論 25 26 ''' 27 結果: 28 Str調用 29 緣由:描述符在使用時被定義成另一個類的類屬性,於是類屬性比二次加工的描述符假裝而來的類屬性有更高的優先級 30 People.name #恩,調用類屬性name,找不到就去找描述符假裝的類屬性name,觸發了__get__() 31 32 People.name='egon' #那賦值呢,直接賦值了一個類屬性,它擁有更高的優先級,至關於覆蓋了描述符,確定不會觸發描述符的__set__() 33 del People.name #同上 34 '''
2)數據描述符--〉實例屬性
1 #描述符Str 2 class Str: 3 def __get__(self, instance, owner): 4 print('Str調用') 5 def __set__(self, instance, value): 6 print('Str設置...') 7 def __delete__(self, instance): 8 print('Str刪除...') 9 10 class People: 11 name=Str() 12 def __init__(self,name,age): #name被Str類代理,age被Int類代理, 13 self.name=name 14 self.age=age 15 16 17 p1=People('egon',18) 18 19 #若是描述符是一個數據描述符(即有__get__又有__set__),那麼p1.name的調用與賦值都是觸發描述符的操做,於p1自己無關了,至關於覆蓋了實例的屬性 20 p1.name='egonnnnnn' 21 p1.name 22 print(p1.__dict__)#實例的屬性字典中沒有name,由於name是一個數據描述符,優先級高於實例屬性,查看/賦值/刪除都是跟描述符有關,與實例無關了 23 del p1.name 24 ''' 25 結果: 26 Str設置... 27 Str設置... 28 Str調用 29 {'age': 18} 30 Str刪除... 31 '''
3)實例屬性--〉非數據描述符
1 # class Foo: 2 # def __set__(self, instance, value): 3 # print('set') 4 # def __get__(self, instance, owner): 5 # print('get') 6 # class Room: 7 # name=Foo() 8 # def __init__(self,name,width,length): 9 # self.name=name 10 # self.width=width 11 # self.length=length 12 # 13 # 14 # #name是一個數據描述符,由於name=Foo()而Foo實現了get和set方法,於是比實例屬性有更高的優先級 15 # #對實例的屬性操做,觸發的都是描述符的 16 # r1=Room('廁所',1,1) 17 # r1.name 18 # r1.name='廚房' 19 ''' 20 set 21 get 22 set 23 ''' 24 25 26 # class Foo: 27 # def __get__(self, instance, owner): 28 # print('get') 29 # class Room: 30 # name=Foo() 31 # def __init__(self,name,width,length): 32 # self.name=name 33 # self.width=width 34 # self.length=length 35 36 37 # #name是一個非數據描述符,由於name=Foo()而Foo沒有實現set方法,於是比實例屬性有更低的優先級 38 # #對實例的屬性操做,觸發的都是實例本身的 39 # r1=Room('廁所',1,1) 40 # r1.name 41 # r1.name='廚房' 42 ''' 43 沒有任何觸發 44 '''
4)非數據描述符--〉找不到的屬性觸發__getattr__()
1 class Foo: 2 def func(self): 3 print('我胡漢三又回來了') 4 5 def __getattr__(self, item): 6 print('找不到了固然是來找我啦',item) 7 f1=Foo() 8 9 f1.xxxxxxxxxxx
五、描述符使用實例
衆所周知,python是弱類型語言,即參數的賦值沒有類型限制,下面咱們經過描述符機制來實現類型限制功能
1 # class Str: 2 # def __init__(self,name): 3 # self.name=name 4 # def __get__(self, instance, owner): 5 # print('get--->',instance,owner) 6 # return instance.__dict__[self.name] 7 # 8 # def __set__(self, instance, value): 9 # print('set--->',instance,value) 10 # instance.__dict__[self.name]=value 11 # 12 # def __delete__(self, instance): 13 # print('delete--->',instance) 14 # instance.__dict__.pop(self.name) 15 # 16 # 17 # class People: 18 # name=Str('name') 19 # def __init__(self,name,age,salary): 20 # self.name=name 21 # self.age=age 22 # self.salary=salary 23 # 24 # p1=People('egon',18,3231.3) 25 ''' 26 set---> <__main__.People object at 0x0000009A73999A20> egon 27 28 ''' 29 30 #調用 31 # print(p1.__dict__) 32 # p1.name 33 ''' 34 {'name': 'egon', 'age': 18, 'salary': 3231.3} 35 get---> <__main__.People object at 0x0000009A73999A20> <class '__main__.People'> 36 ''' 37 38 #賦值 39 # print(p1.__dict__) 40 # p1.name='egonlin' 41 # print(p1.__dict__) 42 ''' 43 {'name': 'egon', 'age': 18, 'salary': 3231.3} 44 set---> <__main__.People object at 0x0000009A73999A20> egonlin 45 {'name': 'egonlin', 'age': 18, 'salary': 3231.3} 46 ''' 47 48 49 #刪除 50 # print(p1.__dict__) 51 # del p1.name 52 # print(p1.__dict__) 53 54 ''' 55 {'name': 'egonlin', 'age': 18, 'salary': 3231.3} 56 delete---> <__main__.People object at 0x0000009A73999A20> 57 {'age': 18, 'salary': 3231.3} 58 '''
添加限制功能:
1 class Typed: 2 def __init__(self,name,expected_type): 3 self.name=name 4 self.expected_type=expected_type 5 def __get__(self, instance, owner): 6 print('get--->',instance,owner) 7 if instance is None: 8 return self 9 return instance.__dict__[self.name] 10 11 def __set__(self, instance, value): 12 print('set--->',instance,value) 13 if not isinstance(value,self.expected_type): 14 raise TypeError('Expected %s' %str(self.expected_type)) 15 instance.__dict__[self.name]=value 16 def __delete__(self, instance): 17 print('delete--->',instance) 18 instance.__dict__.pop(self.name) 19 20 21 class People: 22 name=Typed('name',str) 23 age=Typed('name',int) 24 salary=Typed('name',float) 25 def __init__(self,name,age,salary): 26 self.name=name 27 self.age=age 28 self.salary=salary 29 30 #p1=People(123,18,3333.3) 31 #p1=People('egon','18',3333.3) 32 p1=People('egon',18,3333.3)
經過類裝飾器來實現,中級版:
1 class Typed: 2 def __init__(self,name,expected_type): 3 self.name=name 4 self.expected_type=expected_type 5 def __get__(self, instance, owner): 6 print('get--->',instance,owner) 7 if instance is None: 8 return self 9 return instance.__dict__[self.name] 10 11 def __set__(self, instance, value): 12 print('set--->',instance,value) 13 if not isinstance(value,self.expected_type): 14 raise TypeError('Expected %s' %str(self.expected_type)) 15 instance.__dict__[self.name]=value 16 def __delete__(self, instance): 17 print('delete--->',instance) 18 instance.__dict__.pop(self.name) 19 20 def typeassert(**kwargs): 21 def decorate(cls): 22 print('類的裝飾器開始運行啦------>',kwargs) 23 for name,expected_type in kwargs.items(): 24 setattr(cls,name,Typed(name,expected_type)) 25 return cls 26 return decorate 27 @typeassert(name=str,age=int,salary=float) #有參:1.運行typeassert(...)返回結果是decorate,此時參數都傳給kwargs 2.People=decorate(People) 28 class People: 29 def __init__(self,name,age,salary): 30 self.name=name 31 self.age=age 32 self.salary=salary 33 34 print(People.__dict__) 35 p1=People('egon',18,3333.3)
ps.類裝飾器
a.無參
1 def decorate(cls): 2 print('類的裝飾器開始運行啦------>') 3 return cls 4 5 @decorate #無參:People=decorate(People) 6 class People: 7 def __init__(self,name,age,salary): 8 self.name=name 9 self.age=age 10 self.salary=salary 11 12 p1=People('egon',18,3333.3)
b.有參
1 def typeassert(**kwargs): 2 def decorate(cls): 3 print('類的裝飾器開始運行啦------>',kwargs) 4 return cls 5 return decorate 6 @typeassert(name=str,age=int,salary=float) #有參:1.運行typeassert(...)返回結果是decorate,此時參數都傳給kwargs 2.People=decorate(People) 7 class People: 8 def __init__(self,name,age,salary): 9 self.name=name 10 self.age=age 11 self.salary=salary 12 13 p1=People('egon',18,3333.3)
六、總結
描述符是能夠實現大部分python類特性中的底層魔法,包括@classmethod,@staticmethd,@property甚至是__slots__屬性描述父是不少高級庫和框架的重要工具之一,描述符一般是使用到裝飾器或者元類的大型框架中的一個組件.
七、利用描述符原理完成一個自定製@property,實現延遲計算(本質就是把一個函數屬性利用裝飾器原理作成一個描述符:類的屬性字典中函數名爲key,value爲描述符類產生的對象)
a.@property用法回顧
1 class Room: 2 def __init__(self,name,width,length): 3 self.name=name 4 self.width=width 5 self.length=length 6 7 @property 8 def area(self): 9 return self.width * self.length 10 11 r1=Room('alex',1,1) 12 print(r1.area)
b.本身實現@property功能
1 class Lazyproperty: 2 def __init__(self,func): 3 self.func=func 4 def __get__(self, instance, owner): 5 print('這是咱們本身定製的靜態屬性,r1.area實際是要執行r1.area()') 6 if instance is None: 7 return self 8 return self.func(instance) #此時你應該明白,究竟是誰在爲你作自動傳遞self的事情 9 10 class Room: 11 def __init__(self,name,width,length): 12 self.name=name 13 self.width=width 14 self.length=length 15 16 @Lazyproperty #area=Lazyproperty(area) 至關於定義了一個類屬性,即描述符 17 def area(self): 18 return self.width * self.length 19 20 r1=Room('alex',1,1) 21 print(r1.area)
c.實現延遲計算
1 class Lazyproperty: 2 def __init__(self,func): 3 self.func=func 4 def __get__(self, instance, owner): 5 print('這是咱們本身定製的靜態屬性,r1.area實際是要執行r1.area()') 6 if instance is None: 7 return self 8 else: 9 print('--->') 10 value=self.func(instance) 11 setattr(instance,self.func.__name__,value) #計算一次就緩存到實例的屬性字典中 12 return value 13 14 class Room: 15 def __init__(self,name,width,length): 16 self.name=name 17 self.width=width 18 self.length=length 19 20 @Lazyproperty #area=Lazyproperty(area) 至關於'定義了一個類屬性,即描述符' 21 def area(self): 22 return self.width * self.length 23 24 r1=Room('alex',1,1) 25 print(r1.area) #先從本身的屬性字典找,沒有再去類的中找,而後出發了area的__get__方法 26 print(r1.area) #先從本身的屬性字典找,找到了,是上次計算的結果,這樣就不用每執行一次都去計算
d.去掉裝飾類的返回值,緩存失敗
1 #緩存不起來了 2 3 class Lazyproperty: 4 def __init__(self,func): 5 self.func=func 6 def __get__(self, instance, owner): 7 print('這是咱們本身定製的靜態屬性,r1.area實際是要執行r1.area()') 8 if instance is None: 9 return self 10 else: 11 value=self.func(instance) 12 instance.__dict__[self.func.__name__]=value 13 return value 14 # return self.func(instance) #此時你應該明白,究竟是誰在爲你作自動傳遞self的事情 15 def __set__(self, instance, value): 16 print('hahahahahah') 17 18 class Room: 19 def __init__(self,name,width,length): 20 self.name=name 21 self.width=width 22 self.length=length 23 24 @Lazyproperty #area=Lazyproperty(area) 至關於定義了一個類屬性,即描述符 25 def area(self): 26 return self.width * self.length 27 28 print(Room.__dict__) 29 r1=Room('alex',1,1) 30 print(r1.area) 31 print(r1.area) 32 print(r1.area) 33 print(r1.area) #緩存功能失效,每次都去找描述符了,爲什麼,由於描述符實現了set方法,它由非數據描述符變成了數據描述符,數據描述符比實例屬性有更高的優先級,於是全部的屬性操做都去找描述符了
八、利用描述符原理完成一個自定製@classmethod
1 class ClassMethod: 2 def __init__(self,func): 3 self.func=func 4 5 def __get__(self, instance, owner): #類來調用,instance爲None,owner爲類自己,實例來調用,instance爲實例,owner爲類自己, 6 def feedback(): 7 print('在這裏能夠加功能啊...') 8 return self.func(owner) 9 return feedback 10 11 class People: 12 name='linhaifeng' 13 @ClassMethod # say_hi=ClassMethod(say_hi) 14 def say_hi(cls): 15 print('你好啊,帥哥 %s' %cls.name) 16 17 People.say_hi() 18 19 p1=People() 20 p1.say_hi() 21 #疑問,類方法若是有參數呢,好說,好說 22 23 class ClassMethod: 24 def __init__(self,func): 25 self.func=func 26 27 def __get__(self, instance, owner): #類來調用,instance爲None,owner爲類自己,實例來調用,instance爲實例,owner爲類自己, 28 def feedback(*args,**kwargs): 29 print('在這裏能夠加功能啊...') 30 return self.func(owner,*args,**kwargs) 31 return feedback 32 33 class People: 34 name='linhaifeng' 35 @ClassMethod # say_hi=ClassMethod(say_hi) 36 def say_hi(cls,msg): 37 print('你好啊,帥哥 %s %s' %(cls.name,msg)) 38 39 People.say_hi('你是那偷心的賊') 40 41 p1=People() 42 p1.say_hi('你是那偷心的賊')
九、利用描述符原理完成一個自定製的@staticmethod
1 class StaticMethod: 2 def __init__(self,func): 3 self.func=func 4 5 def __get__(self, instance, owner): #類來調用,instance爲None,owner爲類自己,實例來調用,instance爲實例,owner爲類自己, 6 def feedback(*args,**kwargs): 7 print('在這裏能夠加功能啊...') 8 return self.func(*args,**kwargs) 9 return feedback 10 11 class People: 12 @StaticMethod# say_hi=StaticMethod(say_hi) 13 def say_hi(x,y,z): 14 print('------>',x,y,z) 15 16 People.say_hi(1,2,3) 17 18 p1=People() 19 p1.say_hi(4,5,6)
一個靜態屬性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 AAA=property(get_AAA,set_AAA,delete_AAA) #內置property三個參數與get,set,delete一一對應 11 12 f1=Foo() 13 f1.AAA 14 f1.AAA='aaa' 15 del f1.AAA
三、實例:
1 class Goods: 2 3 def __init__(self): 4 # 原價 5 self.original_price = 100 6 # 折扣 7 self.discount = 0.8 8 9 @property 10 def price(self): 11 # 實際價格 = 原價 * 折扣 12 new_price = self.original_price * self.discount 13 return new_price 14 15 @price.setter 16 def price(self, value): 17 self.original_price = value 18 19 @price.deleter 20 def price(self): 21 del self.original_price 22 23 24 obj = Goods() 25 obj.price # 獲取商品價格 26 obj.price = 200 # 修改商品原價 27 print(obj.price) 28 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') #property自動實現了set和get方法屬於數據描述符,比實例屬性優先級高,因此你這面寫會觸發property內置的set,拋出異常 13 14 15 #第二關:修訂版 16 17 class People: 18 def __init__(self,name): 19 self.name=name #實例化就觸發property 20 21 @property 22 def name(self): 23 # return self.name #無限遞歸 24 print('get------>') 25 return self.DouNiWan 26 27 @name.setter 28 def name(self,value): 29 print('set------>') 30 self.DouNiWan=value 31 32 @name.deleter 33 def name(self): 34 print('delete------>') 35 del self.DouNiWan 36 37 p1=People('alex') #self.name實際是存放到self.DouNiWan裏 38 print(p1.name) 39 print(p1.name) 40 print(p1.name) 41 print(p1.__dict__) 42 43 p1.name='egon' 44 print(p1.__dict__) 45 46 del p1.name 47 print(p1.__dict__) 48 49 50 #第三關:加上類型檢查 51 class People: 52 def __init__(self,name): 53 self.name=name #實例化就觸發property 54 55 @property 56 def name(self): 57 # return self.name #無限遞歸 58 print('get------>') 59 return self.DouNiWan 60 61 @name.setter 62 def name(self,value): 63 print('set------>') 64 if not isinstance(value,str): 65 raise TypeError('必須是字符串類型') 66 self.DouNiWan=value 67 68 @name.deleter 69 def name(self): 70 print('delete------>') 71 del self.DouNiWan 72 73 p1=People('alex') #self.name實際是存放到self.DouNiWan裏 74 p1.name=1
在Python中,若是咱們想實現建立相似於序列和映射的類,能夠經過重寫魔法方法__getitem__、__setitem__、__delitem__、__len__方法去模擬。
__getitem__(self,key):返回鍵對應的值。
__setitem__(self,key,value):設置給定鍵的值
__delitem__(self,key):刪除給定鍵對應的元素。
__len__():返回元素的數量
1 class Foo: 2 def __init__(self,name): 3 self.name=name 4 def __getitem__(self, item): 5 print("getitem") 6 return self.__dict__[item] 7 def __setitem__(self, key, value): 8 print("setitem",key,value) 9 self.__dict__[key]=value 10 def __delitem__(self, key): 11 print('del obj[key]時,我執行') 12 self.__dict__.pop(key) 13 obj=Foo('shuyang') 14 obj.name='shuyang666' 15 print(obj.name) 16 ''' 17 shuyang666 18 ''' 19 print('-----------') 20 obj['name']='shuyang666' 21 ''' 22 setitem name shuyang666 23 ''' 24 print(obj['name']) 25 ''' 26 getitem 27 shuyang666 28 ''' 29 # print(obj.name) 30 del obj['name'] 31 print(obj.__dict__) 32 ''' 33 del obj[key]時,我執行 34 {} 35 '''
一、__str__, __repr__, __format__ 方法
改變對象的字符串顯示__str__,__repr__
自定製格式化字符串__format__
1 ormat_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 class School: 7 def __init__(self,name,addr,type): 8 self.name=name 9 self.addr=addr 10 self.type=type 11 12 def __repr__(self): 13 return 'School(%s,%s)' %(self.name,self.addr) 14 def __str__(self): 15 return '(%s,%s)' %(self.name,self.addr) 16 17 def __format__(self, format_spec): 18 # if format_spec 19 if not format_spec or format_spec not in format_dict: 20 format_spec='nat' 21 fmt=format_dict[format_spec] 22 return fmt.format(obj=self) 23 24 s1=School('oldboy1','北京','私立') 25 print('from repr: ',repr(s1)) 26 print('from str: ',str(s1)) 27 print(s1) 28 29 ''' 30 str函數或者print函數--->obj.__str__() 31 repr或者交互式解釋器--->obj.__repr__() 32 若是__str__沒有被定義,那麼就會使用__repr__來代替輸出 33 注意:這倆方法的返回值必須是字符串,不然拋出異常 34 ''' 35 print(format(s1,'nat')) 36 print(format(s1,'tna')) 37 print(format(s1,'tan')) 38 print(format(s1,'asfdasdffd'))
二、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__:
是一個類變量,變量值能夠是列表,元祖,或者可迭代對象,也能夠是一個字符串(意味着全部實例只有一個數據屬性)。使用點來訪問屬性本質就是在訪問類或者對象的__dict__屬性字典(類的字典是共享的,而每一個實例的是獨立的)
二、__slots__的應用場景:
字典會佔用大量內存,若是你有一個屬性不多的類,可是有不少實例,爲了節省內存可使用__slots__取代實例的__dict__。當你定義__slots__後,__slots__就會爲實例使用一種更加緊湊的內部表示。實例經過一個很小的固定大小的數組來構建,而不是爲每一個實例定義一個。字典,這跟元組或列表很相似。在__slots__中列出的屬性名在內部被映射到這個數組的指定小標上。使用__slots__一個很差的地方就是咱們不能再給實例添加新的屬性了,只能使用在__slots__中定義的那些屬性名。
三、注意事項:
__slots__的不少特性都依賴於普通的基於字典的實現。另外,定義了__slots__後的類再也不 支持一些普通類特性了,好比多繼承。大多數狀況下,你應該只在那些常常被使用到 的用做數據結構的類上定義__slots__好比在程序中須要建立某個類的幾百萬個實例對象 。關於__slots__的一個常見誤區是它能夠做爲一個封裝工具來防止用戶給實例增長新的屬性。儘管使用__slots__能夠達到這樣的目的,可是這個並非它的初衷。更多的是用來做爲一個內存優化工具。
四、寫法示例:
1 class Foo: 2 __slots__='x' 3 4 5 f1=Foo() 6 f1.x=1 7 f1.y=2#報錯 8 print(f1.__slots__) #f1再也不有__dict__ 9 10 class Bar: 11 __slots__=['x','y'] 12 13 n=Bar() 14 n.x,n.y=1,2 15 n.z=3#報錯
五、具體詳解:
http://blog.csdn.net/sxingming/article/details/52892640
在__iter__函數中將使__next__中的StopIteration raise的條件歸零,則能夠循環迭代實例。
一、自增:
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 # for 不報錯 10 n=self.num 11 self.num+=1 12 return n 13 14 f=Foo(1,5) 15 from collections import Iterable,Iterator 16 print(isinstance(f,Iterator)) #是不是一個迭代器 17 18 for i in Foo(1,5): 19 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='') 23 24 斐波那契數列
一、顯示‘ ’ ‘’‘ ’‘’註釋信息
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 print(Bar.__doc__) #該屬性沒法繼承給子類
__module__ 表示當前操做的對象在那個模塊
__class__ 表示當前操做的對象的類是什麼
1 #!/usr/bin/env python 2 # -*- coding:utf-8 -*- 3 4 class C: 5 6 def __init__(self): 7 self.name = ‘SB'
1 from lib.aa import C 2 3 obj = C() 4 print obj.__module__ # 輸出 lib.aa,即:輸出模塊 5 print obj.__class__ # 輸出 lib.aa.C,即:輸出類
析構方法:當對象在內存中被釋放時,自動觸發執行。
注:此方法通常無須定義,由於Python是一門高級語言,程序員在使用時無需關心內存的分配和釋放,由於此工做都是交給Python解釋器來執行,因此,析構函數的調用是由解釋器在進行垃圾回收時自動觸發執行的。
1 class Foo: 2 3 def __del__(self): 4 print('執行我啦') 5 6 f1=Foo() 7 del f1 8 print('------->') 9 10 #輸出結果 11 執行我啦 12 ------->
一、上下文管理協議__enter__、 __exit__(with open('a.txt') as f:)
即with語句,爲了讓一個對象兼容with語句,必須在這個對象的類中聲明__enter__和__exit__方法
1 # class Foo: 2 # def __init__(self, name): 3 # self.name = name 4 # def __enter__(self): 5 # print('__enter__') 6 # return self 7 # 8 # def __exit__(self, exc_type, exc_val, exc_tb): 9 # print('__exit__') 10 # 11 # 12 # with Foo() as x: #x=Foo() 13 # print(x) 14 # print('=>') 15 # print('=>') 16 # print('=>') 17 # print('=>')
二、模擬with...open
1 class Open: 2 def __init__(self,filepath,mode='r',encoding='utf-8'): 3 self.filepath=filepath 4 self.mode=mode 5 self.encoding=encoding 6 7 def __enter__(self): 8 # print('enter') 9 self.f=open(self.filepath,mode=self.mode,encoding=self.encoding) 10 return self.f 11 12 def __exit__(self, exc_type, exc_val, exc_tb): 13 # print('exit') 14 self.f.close() 15 return True 16 def __getattr__(self, item): 17 return getattr(self.f,item) 18 19 with Open('a.txt','w') as f: 20 print(f) 21 f.write('aaaaaa') 22 f.wasdf #拋出異常,交給__exit__處理
ps.__exit__()中的三個參數分別表明異常類型,異常值和追溯信息,with語句中代碼塊出現異常,則with後的代碼都沒法執行
若是__exit()返回值爲True,那麼異常會被清空,就好像啥都沒發生同樣,with後的語句正常執行。
三、用途:
a.使用with語句的目的就是把代碼塊放入with中執行,with結束後,自動完成清理工做,無須手動干預
b.在須要管理一些資源好比文件,網絡鏈接和鎖的編程環境中,能夠在__exit__中定製自動釋放資源的機制,你無須再去關係這個問題,這將大有用處
一、__call__:
對象後面加括號,觸發執行。
注:構造方法的執行是由建立對象觸發的,即:對象 = 類名() ;而對於 __call__ 方法的執行是由對象後加括號觸發的,即:對象() 或者 類()()
1 class Foo: 2 3 def __init__(self): 4 pass 5 6 def __call__(self, *args, **kwargs): 7 8 print('__call__') 9 10 11 obj = Foo() # 執行 __init__ 12 obj() # 執行 __call__
一、exec(srt, global,local)的內置方法:
參數一:字符串形式的命令
參數二:全局做用域
參數三:局部做用域
exec會在指定的局部做用域內執行字符串內的代碼,除非明確地使用global關鍵字。
1 g={ 2 'x':1, 3 'y':2, 4 'teachers':{'egon','alex','yuanhao','wupeiqi'} 5 } 6 l={'birds':[1,2,3]} 7 8 # exec(""" 9 # def func(): 10 # global x 11 # x='egon' 12 # func() 13 # """,g,l) 14 # print(g) 15 # print(l) 16 17 exec(""" 18 global x 19 x='egon' 20 """,g,l) 21 print(g) 22 print(l)
二、類也是對象
python中一切皆是對象,類自己也是一個對象,當使用關鍵字class的時候,python解釋器在加載class的時候就會建立一個對象(這裏的對象指的是類而非類的實例),於是咱們能夠將類看成一個對象去使用,一樣知足第一類對象的概念,能夠:
1 # class Foo: 2 # pass 3 4 5 # obj=Foo() 6 # print(type(obj)) 7 # 8 # print(type(Foo)) 9 # def func(): 10 # class Foo: 11 # pass 12 # 13 # print(Foo) 14 # 15 # func()
三、元類:
元類是類的類,是類的模板。元類是用來控制如何建立類的,正如類是建立對象的模板同樣,而元類的主要目的是爲了控制類的建立行爲。元類的實例化的結果爲咱們用class定義的類,正如類的實例爲對象(f1對象是Foo類的一個實例,Foo類是 type 類的一個實例)type是python的一個內建元類,用來直接控制生成類,python中任何class定義的類其實都是type類實例化的對象。
四、建立類的兩種方式
a.使用class關鍵字:
1 # class Chinese(object): 2 # country='China' 3 # def __init__(self,name,age): 4 # self.name=name 5 # self.age=age 6 # def talk(self): 7 # print('%s is talking' %self.name) 8 # print(Chinese) 9 # print(Chinese.__dict__) 10 # print(Chinese.__bases__)
b.手動使用tpye建立類。
準備工做:
建立類主要分爲三部分
1 類名
2 類的父類
3 類體
1 #類名 2 class_name='Chinese' 3 #類的父類 4 class_bases=(object,) 5 #類體 6 class_body=""" 7 country='China' 8 def __init__(self,name,age): 9 self.name=name 10 self.age=age 11 def talk(self): 12 print('%s is talking' %self.name) 13 """
第一步:生成名稱空間
1 class_dic={} 2 exec(class_body,globals(),class_dic) 3 print(class_dic) 4 #{'country': 'China', 'talk': <function talk at 0x101a560c8>, '__init__': <function __init__ at 0x101a56668>}
第二步:調用元類type(也能夠自定義)來產生類Chinense
1 Foo=type(class_name,class_bases,class_dic) #實例化type獲得對象Foo,即咱們用class定義的類Foo 2 3 print(Foo) 4 print(type(Foo)) 5 print(isinstance(Foo,type)) 6 ''' 7 <class '__main__.Chinese'> 8 <class 'type'> 9 True 10 '''
ps.這樣咱們就手動生成了一個可調用的類對象。
type 接收三個參數:
第 1 個參數是字符串 ‘Foo’,表示類名
第 2 個參數是元組 (object, ),表示全部的父類
第 3 個參數是字典,這裏是一個空字典,表示沒有定義屬性和方法
補充:若Foo類有繼承,即class Foo(Bar):.... 則等同於type('Foo',(Bar,),{})
五、一個類沒有聲明本身的元類,默認他的元類就是type,除了使用元類type,用戶也能夠經過繼承type來自定義元類(順便咱們也能夠瞅一瞅元類如何控制類的建立,工做流程是什麼)
因此類實例化的流程都同樣,與三個方法有關:(大前提,任何名字後加括號,都是在調用一個功能,觸發一個函數的執行,獲得一個返回值)
a.obj=Foo(),會調用產生Foo的類內的__call__方法,Foo()的結果即__call__的結果
b.調用__call__方法的過程當中,先調用Foo.__new__,獲得obj,即實例化的對象,可是尚未初始化
c.調用__call__方法的過程當中,若是Foo.__new__()返回了obj,再調用Foo.__init__,將obj傳入,進行初始化(不然不調用Foo.__init__)
ps:
__new__更像是其餘語言中的構造函數,必須有返回值,返回值就實例化的對象
__init__只是初始化函數,必須沒有返回值,僅僅只是初始化功能,並不能new建立對象
a.在咱們自定義的元類內,__new__方法在產生obj時用type.__new__(cls,*args,**kwargs),用object.__new__(cls)拋出異常:TypeError: object.__new__(Mymeta) is not safe, use type.__new__()
b.在咱們自定義的類內,__new__方法在產生obj時用object.__new__(self)
六、元類控制建立類:
1 class Mymeta(type): 2 def __init__(self): 3 print('__init__') 4 5 def __new__(cls, *args, **kwargs): 6 print('__new__') 7 8 def __call__(self, *args, **kwargs): 9 print('__call__') 10 11 class Foo(metaclass=Mymeta): 12 pass 13 14 print(Foo) 15 ''' 16 打印結果: 17 __new__ 18 None 19 ''' 20 21 ''' 22 分析Foo的產生過程,即Foo=Mymeta(),會觸發產生Mymeta的類內的__call__,即元類的__call__: 23 Mymeta加括號,會觸發父類的__call__,即type.__call__ 24 在type.__call__裏會調用Foo.__new__ 25 而Foo.__new__內只是打印操做,沒有返回值,於是Mymeta的結果爲None,即Foo=None 26 '''
1 class Mymeta(type): 2 def __init__(self,name,bases,dic): 3 for key,value in dic.items(): 4 if key.startswith('__'): 5 continue 6 if not callable(value): 7 continue 8 if not value.__doc__: 9 raise TypeError('%s 必須有文檔註釋' %key) 10 type.__init__(self,name,bases,dic) 11 12 def __new__(cls, *args, **kwargs): 13 # print('__new__') 14 obj=type.__new__(cls,*args,**kwargs) 15 return obj 16 17 def __call__(self, *args, **kwargs): 18 # print('__call__') 19 pass 20 21 # Foo=Mymeta() 22 class Foo(metaclass=Mymeta): 23 def f1(self): 24 'from f1' 25 pass 26 27 def f2(self): 28 pass 29 30 ''' 31 拋出異常 32 TypeError: f2 必須有文檔註釋 33 '''
七、元類控制類建立對象
1 class Mymeta(type): 2 def __call__(self, *args, **kwargs): 3 #self=<class '__main__.Foo'> 4 #args=('egon',) 5 #kwargs={'age':18} 6 obj=self.__new__(self) #建立對象:Foo.__new__(Foo) 7 self.__init__(obj,*args,**kwargs) #初始化對象:Foo.__init__(obj,'egon',age=18) 8 return obj #必定不要忘記return 9 class Foo(metaclass=Mymeta): 10 def __init__(self,name,age): 11 self.name=name 12 self.age=age 13 14 def __new__(cls, *args, **kwargs): 15 return object.__new__(cls,*args,**kwargs) 16 17 obj=Foo('egon',age=18) #觸發Mymeta.__call__ 18 19 print(obj.__dict__)
ps.單例模式:好比數據庫對象,實例化時參數都同樣,就不必重複產生對象,浪費內存
1 #單例模式,好比數據庫對象,實例化時參數都同樣,就不必重複產生對象,浪費內存 2 class Mysql: 3 __instance=None 4 def __init__(self,host='127.0.0.1',port='3306'): 5 self.host=host 6 self.port=port 7 8 @classmethod 9 def singleton(cls,*args,**kwargs): 10 if not cls.__instance: 11 cls.__instance=cls(*args,**kwargs) 12 return cls.__instance 13 14 15 obj1=Mysql() 16 obj2=Mysql() 17 print(obj1 is obj2) #False 18 19 obj3=Mysql.singleton() 20 obj4=Mysql.singleton() 21 print(obj3 is obj4) #True
1 #單例模式,好比數據庫對象,實例化時參數都同樣,就不必重複產生對象,浪費內存 2 class Mymeta(type): 3 def __init__(self,name,bases,dic): #定義類Mysql時就觸發 4 self.__instance=None 5 super().__init__(name,bases,dic) 6 7 def __call__(self, *args, **kwargs): #Mysql(...)時觸發 8 9 if not self.__instance: 10 self.__instance=object.__new__(self) #產生對象 11 self.__init__(self.__instance,*args,**kwargs) #初始化對象 12 #上述兩步能夠合成下面一步 13 # self.__instance=super().__call__(*args,**kwargs) 14 15 return self.__instance 16 class Mysql(metaclass=Mymeta): 17 def __init__(self,host='127.0.0.1',port='3306'): 18 self.host=host 19 self.port=port 20 21 22 obj1=Mysql() 23 obj2=Mysql() 24 25 print(obj1 is obj2)
八、自制元類:
1 class Mytype(type): 2 def __init__(self,class_name,bases=None,dict=None): 3 print("Mytype init--->") 4 print(class_name,type(class_name)) 5 print(bases) 6 print(dict) 7 8 def __call__(self, *args, **kwargs): 9 print('Mytype call---->',self,args,kwargs) 10 obj=self.__new__(self) 11 self.__init__(obj,*args,**kwargs) 12 return obj 13 14 class Foo(object,metaclass=Mytype):#in python3 15 #__metaclass__ = MyType #in python2 16 x=1111111111 17 def __init__(self,name): 18 self.name=name 19 20 def __new__(cls, *args, **kwargs): 21 return super().__new__(cls) 22 # return object.__new__(cls) #同上 23 24 25 f1=Foo('name') 26 print(f1.__dict__)
1 #純淨版 2 class Mytype(type): 3 def __init__(self,what,bases=None,dict=None): 4 print('mytype init') 5 6 def __call__(self, *args, **kwargs): 7 obj=self.__new__(self) 8 self.__init__(obj,*args,**kwargs) 9 return obj 10 11 class Foo(object,metaclass=Mytype): 12 x=1111111111 13 14 def __init__(self,name): 15 self.name=name 16 17 def __new__(cls, *args, **kwargs): 18 return super().__new__(cls) 19 20 f1=Foo('egon') 21 22 print(f1.__dict__)
1 #精簡版 2 class Mytype(type): 3 def __init__(self,what,bases=None,dict=None): 4 print(what,bases,dict) 5 6 def __call__(self, *args, **kwargs): 7 print('--->') 8 obj=object.__new__(self) 9 self.__init__(obj,*args,**kwargs) 10 return obj 11 class Room(metaclass=Mytype): 12 def __init__(self,name): 13 self.name=name 14 15 r1=Room('alex') 16 print(r1.__dict__)
九、元類總結:
1 #元類總結 2 class Mymeta(type): 3 def __init__(self,name,bases,dic): 4 print('===>Mymeta.__init__') 5 6 7 def __new__(cls, *args, **kwargs): 8 print('===>Mymeta.__new__') 9 return type.__new__(cls,*args,**kwargs) 10 11 def __call__(self, *args, **kwargs): 12 print('aaa') 13 obj=self.__new__(self) 14 self.__init__(self,*args,**kwargs) 15 return obj 16 17 class Foo(object,metaclass=Mymeta): 18 def __init__(self,name): 19 self.name=name 20 def __new__(cls, *args, **kwargs): 21 return object.__new__(cls) 22 23 ''' 24 須要記住一點:名字加括號的本質(即,任何name()的形式),都是先找到name的爹,而後執行:爹.__call__ 25 26 而爹.__call__通常作兩件事: 27 1.調用name.__new__方法並返回一個對象 28 2.進而調用name.__init__方法對兒子name進行初始化 29 ''' 30 31 ''' 32 class 定義Foo,並指定元類爲Mymeta,這就至關於要用Mymeta建立一個新的對象Foo,因而至關於執行 33 Foo=Mymeta('foo',(...),{...}) 34 所以咱們能夠看到,只定義class就會有以下執行效果 35 ===>Mymeta.__new__ 36 ===>Mymeta.__init__ 37 實際上class Foo(metaclass=Mymeta)是觸發了Foo=Mymeta('Foo',(...),{...})操做, 38 遇到了名字加括號的形式,即Mymeta(...),因而就去找Mymeta的爹type,而後執行type.__call__(...)方法 39 因而觸發Mymeta.__new__方法獲得一個具體的對象,而後觸發Mymeta.__init__方法對對象進行初始化 40 ''' 41 42 ''' 43 obj=Foo('egon') 44 的原理同上 45 ''' 46 47 ''' 48 總結:元類的難點在於執行順序很繞,其實咱們只須要記住兩點就能夠了 49 1.誰後面跟括號,就從誰的爹中找__call__方法執行 50 type->Mymeta->Foo->obj 51 Mymeta()觸發type.__call__ 52 Foo()觸發Mymeta.__call__ 53 obj()觸發Foo.__call__ 54 2.__call__內按前後順序依次調用兒子的__new__和__init__方法 55 '''
十、元類示例:
a.在元類中控制把自定義類的數據屬性都變成大寫
1 class Mymetaclass(type): 2 def __new__(cls,name,bases,attrs): 3 update_attrs={} 4 for k,v in attrs.items(): 5 if not callable(v) and not k.startswith('__'): 6 update_attrs[k.upper()]=v 7 else: 8 update_attrs[k]=v 9 return type.__new__(cls,name,bases,update_attrs) 10 11 class Chinese(metaclass=Mymetaclass): 12 country='China' 13 tag='Legend of the Dragon' #龍的傳人 14 def walk(self): 15 print('%s is walking' %self.name) 16 17 18 print(Chinese.__dict__) 19 ''' 20 {'__module__': '__main__', 21 'COUNTRY': 'China', 22 'TAG': 'Legend of the Dragon', 23 'walk': <function Chinese.walk at 0x0000000001E7B950>, 24 '__dict__': <attribute '__dict__' of 'Chinese' objects>, 25 '__weakref__': <attribute '__weakref__' of 'Chinese' objects>, 26 '__doc__': None} 27 '''
b.在元類中控制自定義的類無需__init__方法
1.元類幫其完成建立對象,以及初始化操做;
2.要求實例化時傳參必須爲關鍵字形式,不然拋出異常TypeError: must use keyword argument for key function;
3.key做爲用戶自定義類產生對象的屬性,且全部屬性變成大寫
1 class Mymetaclass(type): 2 # def __new__(cls,name,bases,attrs): 3 # update_attrs={} 4 # for k,v in attrs.items(): 5 # if not callable(v) and not k.startswith('__'): 6 # update_attrs[k.upper()]=v 7 # else: 8 # update_attrs[k]=v 9 # return type.__new__(cls,name,bases,update_attrs) 10 11 def __call__(self, *args, **kwargs): 12 if args: 13 raise TypeError('must use keyword argument for key function') 14 obj = object.__new__(self) #建立對象,self爲類Foo 15 16 for k,v in kwargs.items(): 17 obj.__dict__[k.upper()]=v 18 return obj 19 20 class Chinese(metaclass=Mymetaclass): 21 country='China' 22 tag='Legend of the Dragon' #龍的傳人 23 def walk(self): 24 print('%s is walking' %self.name) 25 26 27 p=Chinese(name='egon',age=18,sex='male') 28 print(p.__dict__)