python-----面向物件進階
一、反射
概念:主要是指程式可以訪問、檢測和修改它本身狀態或者行為的一種能力(自省)
python面向物件中的反射:通過字串的形式操作物件的相關屬性。python中的一切事物皆物件(都可以使用反射)
一、四個可以實現自省的函式:
hasattr(obj,'屬性名') 檢測物件是否含有某屬性
getattr(obj,‘屬性名’,100)通過字串型別獲取物件的某個屬性,不存在則會報錯,可定義第三個引數的預設值,不存在則會顯示第三個引數,不會報錯。
setattr(obj,'屬性名',屬性)設定屬性,若屬性不存在則會新添新的屬性,存在則會修改為新的屬性
delattr(obj,‘屬性名’)刪除物件的某個屬性,不存在則會報錯
1 # _*_ encoding:utf-8 _*_ 2 __author__ = 'listen' 3 __date__ = '2018/12/1 13:09' 4 # class BlackMedium: 5 # feture='ugly' 6 # def __init__(self,name,addr): 7 # self.name=name 8 # self.addr=addr 9 # def rent_hourse(self): 10 # print('[%s]出租房子,不要去租!'%self.name)11 # def sell_hourse(self): 12 # print('[%s]賣房子,不要去買!'%self.name) 13 # b=BlackMedium('萬成置地','天露園') 14 # print(hasattr(b,'name')) #有沒有name屬性 找的是對應的key 15 # print(hasattr(b,'addr')) 16 17 # print(getattr(b,'name')) #萬成置地 得到這個物件的屬性值===相當於b.name 18 # getattr(b,'sell_hourse')() #同樣也可以 19# print(getattr(b,'age')) #沒有這個屬性就會報錯,不過可以傳一個預設值 20 # print(getattr(b,'age',23)) #沒有這個屬性值會顯示預設的值,不會報錯 23 21 22 # setattr(b,'name','小黑') 23 # print(b.__dict__) #name的值就從萬成置地變為小黑 修改了一個屬性的值 24 # setattr(b,'age',5) 25 # # print(b.__dict__) #{'name': '萬成置地', 'addr': '天露園', 'age': 5} 添加了一個屬性 26 # # 27 # # delattr(b,'age') 28 # # print(b.__dict__) #刪除了一個屬性/2反射的優點:
二、反射的優點
1、通過反射,可以實現即插即用,定義介面的功能(可以事先把邏輯寫好(只定義介面),然後後期再去實現介面功能)
‘alex’應該寫的程式碼
1 # _*_ encoding:utf-8 _*_ 2 __author__ = 'listen' 3 __date__ = '2018/12/1 14:16' 4 class FtpClient: 5 'ftp客戶端,但沒有實現功能' 6 def __init__(self,addr): 7 self.addr=addr 8 def load(self): 9 print('上傳照片')
‘listen’應該寫的程式碼
1 # _*_ encoding:utf-8 _*_ 2 __author__ = 'listen' 3 __date__ = '2018/12/1 14:16' 4 #1通過反射,可以實現即插即用,定義介面的功能(可以事先把主要邏輯寫好(只定義介面),後期再去實現介面功能。) 5 # from alex_module import FtpClient 6 # from alex_module import * 7 # f1=FtpClient('172.16.1.3') 8 # if hasattr(f1,'load'): 9 # f1.load() #'FtpClient' object has no attribute 'load' 報錯,但不影響,兩個人可以相互獨立工作,等第一個人寫好了,直接呼叫了 10 # else: 11 # print('沒有load這個方法')
這樣實現程式碼互不影響。
2、動態匯入模組(基於反射當前的模組成員)
1 #2 動態匯入模組 底層基於反射 包中__init__檔案 2 # from hell.test import * #一匯入模組直接執行模組中的內容 3 # from hell.test import * #內容中有私有的屬性 _test1() 4 # from hell.test import test,_test1,test2 #這樣就可以運行了 *對於私有的導不進去,只能一個一個把函式導進去 5 # test() #這個可以執行 6 # _test1() #這個執行不了 7 # test2() 8 #執行結果: 9 # ----------》hello 10 # --->test1 11 # --->test2 12 # --->test3 13 14 #模組是一個字元換'hell.test' 怎麼匯入字串模組 15 # module=__import__('hell.test') 16 # print(module) #只能識別到hell模組中不能再識別下一級模組 17 18 19 # import importlib 20 # m=importlib.import_module('hell.test') #可以識別到hell模組的下一級test 21 # # print(m)
三、__setattr__、__getattr__、__delattr__內建的方法
__getattr__在使用的時候,沒有例項的這個屬性的時候才會被觸發,有例項的屬性不會觸發這個方法。------>>>最常用的方法
1 class Foo: 2 country='china' 3 def __init__(self,name): 4 self.name=name 5 # def __getattr__(self, item): #item就是不存在的屬性 6 # print('執行__getattr__') 7 f=Foo('alex') #設定一遍執行一遍 8 # # print(f.name) 9 # # print(f.age) #如果沒有這個屬性 會觸發__getattr__ 有這個屬性則不會觸發
__setattr__:在設定值得時候就會觸發 -------------->設定屬性的過程,可以裡面自定義屬性
1 class Foo: 2 country='china' 3 def __init__(self,name): 4 self.name=name 5 # 6 # def __setattr__(self, key, value): 7 # print('執行__setattr__')
#直到這沒有意義不會新增屬性到屬性字典中,內建的__setattr__會新增屬性字典中
8 # # self.key=value 錯誤用法 9 # self.__dict__[key]=value #在底層字典設定,本來就在底層操作字典 10 11 f=Foo('alex') #設定一遍執行一遍 12 # f.age=18 13 # f.sex='male' 14 # print(f.__dict__)
__delattr__刪除屬性的時候會觸發
1 class Foo: 2 country='china' 3 def __init__(self,name): 4 self.name=name 5 6 # 7 def __delattr__(self, item): 8 print('執行__delattr__') 9 self.__dict__.pop(item) 10 f=Foo('alex') #設定一遍執行一遍 11 print(f.__dict__) 12 del f.name #只要刪除就會觸發 13 print(f.__dict__)
四、包裝和授權
包裝:python為我們提供了標準資料型別,以及豐富的內建方法,其實很多場景下,我們都需要基於標準的資料型別來定製我們自己的資料型別,新增或改寫方法,這就需要繼承和派生了(其他標準型別均可以通過下面的方式進行二次加工)
1 # _*_ encoding:utf-8 _*_ 2 __author__ = 'listen' 3 __date__ = '2018/12/2 8:51' 4 #包裝 5 #繼承、派生、改寫父類的方法 6 class List(list): 7 #改寫父類list的方法 8 def append(self, object): 9 if type(object) is str: 10 # return list.append(self,object) #呼叫父類的方法 11 return super().append(object) #不用傳self 12 else: 13 return '不是字串型別,不能新增成功!' 14 #取中間值 派生 15 def get_middle(self): 16 mid_num=int(len(self)/2) 17 return self[mid_num] 18 19 l=List('1234567') 20 # l.append('b') 21 print(l.get_middle())
授權:授權是包裝的一個特性,包裝一個型別通常是對已存在的型別的一些定製,這種做法可以新建、修改或刪除原有的功能,其他的保持原樣。授權的過程,即是所有更新的功能都是由新類的某部分來處理,但已存在的功能就授權給物件預設屬性。
實現授權的關鍵就是覆蓋__getattr__方法
1 # _*_ encoding:utf-8 _*_ 2 __author__ = 'listen' 3 __date__ = '2018/12/2 9:23' 4 import time 5 #授權 6 class FileHandle: 7 def __init__(self,filename,mode,encoding='utf-8'): 8 self.file=open(filename,mode,encoding=encoding) #類似於組合 9 self.mode=mode 10 self.encoding=encoding 11 12 #實現重寫功能 13 # def write(self,line): 14 # print('不允許寫入',line) #自己有這個方法先執行自己的方法 15 #實現加上時間的操作 16 def write(self,line): 17 t=time.strftime('%Y-%m-%d %X') 18 self.file.write('%s%s'%(t,line)) 19 20 #實現了繼承功能 21 def __getattr__(self,item): 22 # print('----->',item) 23 # print(type(item)) #str型別變為屬性 getattr()可以做到 24 return getattr(self.file,item) 25 26 f=FileHandle('a.txt','r+') 27 f.write('負載過大\n') 28 f.write('記憶體不足\n') 29 f.write('硬碟記憶體不足') 30 # f.seek(0) 31 # print(f.read())
五、__getattrabute__用法
1 # _*_ encoding:utf-8 _*_ 2 __author__ = 'listen' 3 __date__ = '2018/12/2 12:56' 4 class Foo: 5 def __init__(self,name): 6 self.name=name 7 def __getattr__(self, item): #小弟 8 print('----->from getattr') 9 def __getattribute__(self, item): #大哥 10 print('----------->from getattribute') 11 raise AttributeError('丟擲異常了!') #attribute和attr的一個通訊 12 f=Foo('alex') 13 # f.name #有這個屬性執行getattribute 14 f.age #沒有這個屬性也執行 getattribute 沒執行getattr 但是加上AttributeError之後,大哥和小弟之間就可以通話了,然後再去找attr,然後就執行attr,當沒定義getattrate,系統就會呼叫自己的getattrate,然後看的是隻執行了attr(其實內部已經執行了getattrabute)
六、__setitem__、__getitem__、__delitem__
字典設定屬性
1 # _*_ encoding:utf-8 _*_ 2 __author__ = 'listen' 3 __date__ = '2018/12/2 13:35' 4 # . 形式的操作是會觸發__setattr__ __getattr__ __delattr__ 字典的形式是操作__setitem__ __getitem__ __delitem__ 5 #都是以字典的形式找、刪、改 6 class Foo: 7 def __init__(self,name): 8 self.name=name 9 def __getitem__(self, item): 10 print('--------->from getitem') 11 return self.__dict__[item] 12 13 def __setitem__(self, key, value): 14 print('---->from setitem') 15 self.__dict__[key]=value 16 17 def __delitem__(self, key): 18 print('----->from delitem') 19 self.__dict__.pop(key) 20 21 f=Foo('alex') 22 f['age']=17 #觸發setitem 新增一個新的屬性age 23 print(f['name']) #觸發getitem 尋找屬性的時候 24 del f['age'] #會觸發delitem 刪除屬性 25 print(f.__dict__)
七、__str__、__repr__、__format__ ====str() repr() format()
1 # _*_ encoding:utf-8 _*_ 2 __author__ = 'listen' 3 __date__ = '2018/12/2 13:49' 4 #__str__方法 5 # class Foo: 6 # def __init__(self,name,age): 7 # self.name=name 8 # self.age=age 9 # def __str__(self): 10 # return '名字是%s,年齡是%s'%(self.name,self.age) 11 # 12 # f=Foo('listen',18) 13 # print(f) #<__main__.Foo object at 0x000001A72D16BCC0>記憶體地址 不新增__str__方法 14 # print(f) #添加了__str__方法 --》名字是listen,年齡是18 str(obj)===obj.__str__() 和len(l)===l.__len__() print列印 其實是呼叫__str__()方法 15 16 #__repr__方法 17 class Foo: 18 def __init__(self,name,age): 19 self.name=name 20 self.age=age 21 def __repr__(self): 22 return '名字n是%s,年齡a是%s'%(self.name,self.age) 23 24 f=Foo('listen',18) 25 print(f) #名字n是listen,年齡a是18 print一列印,先找__str__方法,沒有定義的__str__方法如果有__repr__,再去用__repr__代替
__format__ 格式化字串
1 # _*_ encoding:utf-8 _*_ 2 __author__ = 'listen' 3 __date__ = '2018/12/2 14:40' 4 format_dic={ 5 'ymd':'{0.year}{0.month}{0.day}', 6 'y:m:d':'{0.year}:{0.month}:{0.day}', 7 'y-m-d':'{0.year}-{0.month}-{0.day}' 8 } 9 10 class Date: 11 def __init__(self,year,month,day): 12 self.year=year 13 self.month=month 14 self.day=day 15 def __format__(self, format_spec): 16 # print('我開始執行了') 17 if not format_spec or format_spec not in format_dic: 18 n=format_dic['y:m:d'] 19 return n.format(self) 20 m=format_dic[format_spec] 21 return m.format(self) 22 23 24 d=Date(2018,12,2) 25 # print(format(d,'2')) #得到的是一個format_spec 26 # ymd='{0.year}{0.month}{0.day}'.format(d) #format(d)===d.__format__() 27 # ydm='{0.year}:{0.month}:{0.day}'.format(d) 28 # dym='{0.year}-{0.month}-{0.day}'.format(d) 29 # print(ydm) 30 print(format(d,'y-m-d')) #2018-12-2 觸發內部的__format__方法 31 print(format(d,'ymd')) #2018122 32 print(format(d,'asd')) 33 print(format(d)) #2018:12:2
八、isinstance(obj,cls)和issubclass(sub,super)
isinstance(p,A) 判斷p是否是類A的物件,返回布林值
issubclass(B,A) 判斷類B是否繼承類A,即B是否是A的派生類返回布林值。
1 class A: 2 pass 3 class B(A): 4 pass 5 p=A() 6 print(isinstance(p,A)) #True 7 print(issubclass(B,A)) #True
九、__slots__
1、__slots__是什麼:是一個類變數,變數值可以為列表、元組、或者可迭代物件,也可以為一個字串(意味著所有例項只有一個數據屬性)
2、引子:使用點來訪問屬性本質就是在訪問類或者物件的__dict__屬性字典(類的字典是共享的而例項的屬性是獨立的)
3、使用__slots__優點:字典會佔用大量記憶體,如果你有一個屬性很少的類,但是有很多例項,為了節省記憶體可以使用__slots__取代例項的__dict__
當你定義__slots__後,__slots__就會為例項使用一種更緊湊的內部表示。例項通過一個很凶啊的固定大小的陣列來構建,而不是為每個例項定義一個。
字典,這跟列表和元組很類似,在__slots__中列出的屬性名在內部被對映到這個陣列的小標上。使用__slots__一個不好的地方就是我們不能在給例項新增新的屬性,只能使用在__slots__定義的屬性。
4、注意事項:__slots__的很多特性依賴於普通字典的實現。另外,定義了__slots__後的類不再支援一些普通的特性,比如多繼承,大多數情況下,你應該只在那些經常被使用到的用作資料結構的類上定義__slots__,比如在程式中需要建立某個類的成千上百個例項物件。
關於__slots__的一個常見誤區是它可以作為一個封裝工具來防止使用者給例項增加新的屬性。儘管可以達到這樣的目的,但並不是它的初衷。 更多的是用來作為一個記憶體優化的用具。
1 # _*_ encoding:utf-8 _*_ 2 __author__ = 'listen' 3 __date__ = '2018/12/3 21:03' 4 # #類的屬性很少 但建立了好多例項 為了防止記憶體利用率低,可這樣節省記憶體 取消例項字典的功能 但類還有字典的功能 5 # class Foo: 6 # # __slots__ = 'name' #可以為字串也可以為字典 7 # __slots__ = ['name','age'] 8 # # f1=Foo() 9 # f2=Foo() 10 # f3=Foo() 11 # # f1.name='alex' 12 # # print(f1.name) 13 # # f1.age=18 14 # # print(f1.age) #會報錯 限制了例項的屬性 節省記憶體 例項沒有自己的屬性只能根據類定義的__slots__屬性新增自己的屬性,把底層字典的功能取消掉 15 # f2.name='listen' 16 # f2.age=18 17 # f3.age=20 18 # 19 # print(f3.age) 20 # 21 # print(f2.name,f2.age) 22 # print(f2.__slots__)
十、__doc__
1 #__doc__屬性不被繼承 2 # class A: 3 # "這是文件描述資訊" 4 # pass 5 # class B(A): 6 # pass 7 # print(A.__dict__) 8 # print(A.__doc__) #這是文件資訊 9 # print(B.__doc__) #None 10 # print(B.__dict__) #{'__module__': '__main__', '__doc__': None}
十一、析構方法 __del__
__del__:析構方法,當物件在記憶體中被釋放時,自動觸發執行。此方法無需定義,解構函式的呼叫是由直譯器在進行垃圾回收時自動觸發執行的。
1 # _*_ encoding:utf-8 _*_ 2 __author__ = 'listen' 3 __date__ = '2018/12/3 21:42' 4 # class Foo: 5 # def __init__(self,name): 6 # self.name=name 7 # 8 # def __del__(self): 9 # print('我執行了!') 10 # f=Foo('alex') 11 # del f 12 # print('---------------') 13 #result: 14 # 我執行了! 15 # --------------- 16 17 class Foo: 18 def __init__(self,name): 19 self.name=name 20 21 def __del__(self): 22 print('我執行了!') 23 f=Foo('alex') 24 del f.name 25 print('---------------') 26 # result: 27 # --------------- 28 # 我執行了! 刪掉例項的屬性時不會觸發解構函式,刪掉物件才會觸發 但是當程式執行完畢後 垃圾回收 所以會觸發釋放記憶體
十二、__call__
物件後面加括號,觸發執行 。
注:構造方法的執行是由建立物件觸發的,即:物件=類名();而對於__call__方法的執行是由物件加()觸發的,即物件() 類名()() 一切皆物件,其實類也是物件
1 # _*_ encoding:utf-8 _*_ 2 __author__ = 'listen' 3 __date__ = '2018/12/3 21:53' 4 class Foo: 5 def __call__(self, *args, **kwargs): 6 print('我執行啦') 7 f=Foo() 8 f() #會觸發例項的類的__call__方法 我執行啦 9 Foo() #一切皆物件 Foo加()也會觸發它下面的object類的__call方法
十三、__module__和__class__用法
__module__:表示當前操作的物件在那個模組下(import的模組直接用例項)
__class__:表示當前操作的物件的類是什麼
1 # _*_ encoding:utf-8 _*_ 2 __author__ = 'listen' 3 __date__ = '2018/12/3 21:26' 4 class Foo: 5 def __init__(self,name,age): 6 self.name=name 7 self.age=age
引用上面的模組去建立物件
2 # _*_ encoding:utf-8 _*_ 3 __author__ = 'listen' 4 __date__ = '2018/12/3 21:27' 5 from module_class.module import Foo 6 f=Foo('alex',18) 7 print(f.name) 8 print(f.__module__) #module_class.module 9 print(f.__class__) #<class 'module_class.module.Foo'>
十四、__iter__和__next__實現迭代器協議
1 # _*_ ecoding:utf-8 _*_ 2 class A: 3 def __init__(self,num): 4 self.num=num 5 def __iter__(self): 6 return self 7 def __next__(self): 8 if self.num == 5: 9 raise StopIteration('異常終止') 10 self.num+=1 11 return self.num 12 a=A(2) 13 # print(a.__next__()) 14 # print(a.__next__()) 15 # print(a.__next__()) 16 # print(a.__next__()) 17 for i in a: 18 print(i) #for 先呼叫__iter__方法,變為可迭代物件之後再內部執行__next__方法,但是內部可以到不滿足條件終止,迴圈列印,捕捉異常退出
十五、描述符(__get__、__set__、__delete__)
描述符本質上就是一個新式類,在這個新式類中,至少實現了__get__()、__set__()、__delete__()方法中的一個,這也被稱為描述符協議。
__get__():呼叫一個屬性時,觸發
__set__():為一個屬性賦值時,觸發
__delete__():採用del刪除一個屬性時,觸發
1 # _*_ encoding:utf-8 _*_ 2 __author__ = 'listen' 3 __date__ = '2018/12/4 20:45' 4 #這個類是一個描述符 5 class Foo: 6 def __get__(self, instance, owner): 7 print('get method') 8 def __set__(self, instance, value): 9 print('set method',instance,value) 10 # instance.__dict__['x']=value 11 12 def __delete__(self, instance): 13 print('delete method') 14 #描述符是用來代理另外一個類的屬性(必須把描述符定義到另一個類的類屬性中,不能定義到建構函式中),描述符只能用來定義描述其他的類,自己的類不行 15 #只要代理的屬性統統歸代理管,沒有代理的屬性統統和代理無關 16 class Bar: 17 x=Foo() #在何地 18 # def __init__(self,x): 19 # self.x=x ===b.x=10 20 b=Bar() 21 b.x=10 #呼叫類屬性並賦值 set method {'x': 10} 22 print(b.__dict__) #{} 空的 x被Foo代理了,所以為空 因為__set__方法中什麼都沒有做,沒有做底層字典的操作 23 # b.x #get method 呼叫類屬性 24 # del b.x #delete method 刪除類屬性
描述符是幹什麼用的?
描述符的作用是用來定義另外一個類的屬性的(必須把描述符定義成這個類的類屬性,不能定義到建構函式中),描述符只能用來代理除描述符以外的其他類
Bar這個類被Foo類代理----一般從例項物件中可以看出來
描述符分為兩種:1、資料描述符:至少實現__get__和__set__
2、非資料描述符:沒有實現__set__
注意事項:一、描述符本身應該定義為新式類,被代理的類也應該是新式類
二、必須把描述符定義成另一個類的類屬性,不能定義到建構函式中
三、要嚴格遵守該優先順序,優先順序從高到低是:
類屬性
資料描述符
例項屬性
非資料描述符
找不到的屬性觸發__getattr__()
1 # _*_ encoding:utf-8 _*_ 2 __author__ = 'listen' 3 __date__ = '2018/12/4 21:31' 4 class Foo: 5 def __get__(self, instance, owner): 6 print('get method') 7 # def __set__(self, instance, value): 8 # print('set method',instance,value) 9 # # instance.__dict__['x']=value 10 # 11 # def __delete__(self, instance): 12 # print('delete method') 13 class Bar: 14 x=Foo() #在何地 15 16 b=Bar() 17 # print(Bar.x) #get method 18 # Bar.x=1 19 # print(Bar.x) #1 20 # print(Bar.__dict__) #裡面有x=1屬性 說明類屬性>資料描述符(有get和set方法) 21 22 # b.x #觸發get method 23 # b.x=10 #觸發 set method <__main__.Bar object at 0x00000235855FBE48> 10 並不是給例項重點新增一個屬性 24 # print(b.__dict__) #{} 資料描述符> 例項 25 26 # b.x #觸發 get method 27 # b.x=10 28 # print(b.__dict__) #{'x': 10} 例項>非資料描述符 29 30 b.x #get method 31 print(b.yyyyy) #觸發__getattr__ 非資料>找不到
描述符的使用:
1、由於python是弱型別語言,所以不用定義屬性的資料型別就可以直接使用,描述符可以實現型別的限制。
1 # _*_ encoding:utf-8 _*_ 2 __author__ = 'listen' 3 __date__ = '2018/12/6 20:38' 4 # class Typed: 5 # def __get__(self, instance, owner): 6 # print('這是get方法') 7 # print('這是instance%s'%instance) 8 # print('這是owner%s'%owner) 9 # def __set__(self, instance, value): 10 # print('這是set方法') 11 # print('這是instance%s'%instance) 12 # print('這是owner%s'%value) 13 # def __delete__(self, instance): 14 # print('這是delete方法') 15 # print('這是instance%s'%instance) 16 # 17 # 18 # class People: 19 # name=Typed() 20 # def __init__(self,name,age,salary): 21 # self.name=name 22 # self.age=age 23 # self.salary=salary 24 # p=People('alex',18,1000.6) # 這是set方法 這是instance<__main__.People object at 0x000001FD8B301550> 這是owneralex 賦值的所有者是附的值 25 # print(p) #<__main__.People object at 0x000001FD8B301550> 26 # p.name #這是get方法 這是instance<__main__.People object at 0x000002C3D23A1550> 這是owner<class '__main__.People'> 呼叫(物件的所有者是類) 27 # p.name='listen' #這是set方法 這是instance<__main__.People object at 0x0000024E7C251588> 這是ownerlisten 28 # print(p.__dict__) #{'age': 18, 'salary': 1000.6}
1 # _*_ encoding:utf-8 _*_ 2 __author__ = 'listen' 3 __date__ = '2018/12/6 20:38' 4 5 #對傳入屬性的值型別限制 name只能傳入字串 6 # class Typed: 7 # def __init__(self,key): 8 # self.key=key 9 # def __get__(self, instance, owner): 10 # print('這是get方法') 11 # return instance.__dict__[self.key] 12 # 13 # def __set__(self, instance, value): 14 # print('這是set方法') 15 # if not isinstance(value, str): 16 # raise TypeError('型別不是字串') 17 # instance.__dict__[self.key]=value 18 # def __delete__(self, instance): 19 # print('這是delete方法') 20 # instance.__dict__.pop(self.key) 21 # 22 # class People: 23 # name=Typed('name') 24 # # age = Typed('age') 25 # def __init__(self,name,age,salary): 26 # self.name=name 27 # self.age=age 28 # self.salary=salary 29 # p=People('alex',18,1000.6) 30 # print(p.__dict__) #{'name': 'alex', 'age': 18, 'salary': 1000.6} 31 # p=People(22,18,1000.6) 32 # print(p.__dict__) #TypeError: 型別不是字串 肯定加不到字典中 33 # p.name='listen' 34 # print(p.__dict__) #{'name': 'listen', 'age': 18, 'salary': 1000.6} 新增進去了
1 #再加一個限制age的型別 2 class Typed: 3 def __init__(self,key,except_key): 4 self.key=key 5 self.except_key=except_key 6 def __get__(self, instance, owner): 7 print('這是get方法') 8 return instance.__dict__[self.key] 9 10 def __set__(self, instance, value): 11 print('這是set方法') 12 if not isinstance(value,self.except_key): 13 raise TypeError('型別不是%s'%self.except_key) 14 instance.__dict__[self.key]=value 15 def __delete__(self, instance): 16 print('這是delete方法') 17 instance.__dict__.pop(self.key) 18 19 class People: 20 name=Typed('name',str) 21 age = Typed('age',int) 22 def __init__(self,name,age,salary): 23 self.name=name 24 self.age=age 25 self.salary=salary 26 # p=People('alex',18,1000.6) #這是set方法 這是set方法 27 # p=People(10,18,1000.6) #TypeError: 型別不是<class 'str'> 28 p=People('alex','j',1000.6) #TypeError: 型別不是<class 'int'>
2、實現裝飾器功能
基本的裝飾器:
1 # _*_ encoding:utf-8 _*_ 2 __author__ = 'listen' 3 __date__ = '2018/12/6 22:18' 4 # def deco(obj): 5 # obj.x=1 6 # obj.y=2 7 # obj.z=3 8 # return obj 9 # @deco 10 # class Foo: 11 # pass 12 # print(Foo.__dict__) #屬性字典中有 x y z 13 #傳的引數是活的 14 def Type(**kwargs): #在外面巢狀一個函式 實現加引數的功能 15 def deco(obj): 16 for key,val in kwargs.items(): 17 setattr(obj,key,val) 18 return obj 19 return deco 20 @Type(age=17,name='alex') 21 class Foo: 22 pass 23 print(Foo.age,Foo.name) #17 alex
裝飾器的補充(和描述符沒關係,只是單純的裝飾器的補充):
1 # _*_ encoding:utf-8 _*_ 2 __author__ = 'listen' 3 __date__ = '2018/12/8 10:13' 4 # class Dog: 5 # def __init__(self,name): 6 # self.name=name 7 # @property 8 # def like_wang(self): 9 # print( 'get汪汪汪') 10 # @like_wang.setter 11 # def like_wang(self,value): 12 # print('set汪汪汪',value) 13 # @like_wang.deleter 14 # def like_wang(self): 15 # print('del汪汪汪') 16 # 17 # d=Dog('豆豆') 18 # # print(d.like_wang) 19 # # d.like_wang='wolf' #AttributeError: can't set attribute 20 # del d.like_wang 21 22 #商品的價格變動 23 # class Goods: 24 # def __init__(self,orign_price,zhekou): 25 # self.orign_price=orign_price 26 # self.zhekou=zhekou 27 # @property 28 # def price(self): 29 # return self.orign_price*self.zhekou 30 # @price.setter 31 # def price(self,val): 32 # self.orign_price=val 33 # 34 # @price.deleter 35 # def price(self): 36 # del self.orign_price 37 # 38 # 39 # g=Goods(100,0.5) 40 # # print(g.price) #50 41 # # g.price=200 42 # # print(g.price) #100 43 # del g.price 44 # print(g.price) 45 46 47 #加上型別限制 48 class Goods: 49 def __init__(self,orign_price,zhekou): 50 self.orign_price=orign_price 51 self.zhekou=zhekou 52 @property 53 def price(self): 54 return self.orign_price*self.zhekou 55 @price.setter 56 def price(self,val): 57 if val is not int: 58 raise TypeError('此型別應該為int') 59 self.orign_price=val 60 61 @price.deleter 62 def price(self): 63 del self.orign_price 64 # a=property(get_price,set_price,del_price) 第2種使用方法 65 66 g=Goods(100,0.5) 67 g.price='lll' 68 print(g.price) # 此型別應該為int 69 # print(g.price) #50 70 # g.price=200 71 # print(g.price) #100 72 # print(g.a) #50
十六、__enter__和__exit__
檔案的操作方式:
1 #此方法不需要自己關掉檔案 2 with open('檔名','開啟方式','編碼方式') as f: 3 '程式碼塊' #檔案操作
上述叫做上下文管理協議,即with語句,為了讓一個物件相容with語句,必須在這個物件的類中宣告__enter__和__exit__方法
1 # _*_ encoding:utf-8 _*_ 2 __author__ = 'listen' 3 __date__ = '2018/12/5 21:58' 4 class Open: 5 def __init__(self,name): 6 self.name=name 7 def __enter__(self): 8 print('---->enter') 9 return self 10 def __exit__(self, exc_type, exc_val, exc_tb): 11 print('---------->exit') 12 with Open('a') as f : #__enter__返回的值會給f 這一步是執行enter f=self 物件的例項化 13 print(f) #列印物件的地址 14 print(f.name) #a 15 print('----->') 16 print('----->') 17 print('----->') #中間都在執行檔案的操作 18 print('----->') 19 print('----->') #關閉檔案 執行完內部後會觸發__exit__ 20 print('==========>>')
在with的程式碼塊中,列印未定義的變數,觸發exit方法,待exit函式執行結束,代表with程式碼塊也執行結束,報錯資訊列印,整個程式結束;但是在exit方法中加上return True,就會繼續執行with程式碼塊外的語句,讓外部看不出裡面有什麼報錯,隱藏報錯資訊。
1 class Open: 2 def __init__(self,name): 3 self.name=name 4 def __enter__(self): 5 print('---->enter') 6 return self 7 def __exit__(self, exc_type, exc_val, exc_tb): #exc_type 異常的類 exc_val 異常的值 exc_tb 異常的追蹤資訊 8 print('---------->exit') 9 print(exc_type) #<class 'NameError'> 10 print(exc_val) # NameError: name 'adghh' is not defined name 'adghh' is not defined 11 print(exc_tb) #<traceback object at 0x000001E7A7FB9048> 12 return True 13 with Open('a') as f : #__enter__返回的值會給f 這一步是執行enter f=self 14 print(f) #列印物件的地址 15 print(f.name) #a 16 print(adghh) #列印未定義的變數 觸發__exit__程式會退出 列印錯誤資訊 17 print('----->') #關閉檔案 執行完內部後會觸發__exit__ 18 19 print('==========>>') #這將會被執行 把錯誤吞掉 除了內部知道有問題,外部看不出來,不影響外部程式的執行
小結:
1、with obj---->代表觸發obj.__enter__(),拿到返回值
2、as f----->f=返回值
3、with obj as f ===f=obj.__enter__()
4、執行程式碼塊
①沒有異常的情況下,整個程式碼塊執行完畢後去觸發__exit__.
②有異常的情況下,從異常出現的位置觸發__exit__
a:如果__exit__返回值是True 代表吞掉了異常
b:如果__exit__返回值不為True,代表吐出了異常
__exit__的執行完畢就代表整個with語句的執行完畢
用途或者好處:
1、使用with語句的目的就是把程式碼塊放入with中執行,with結束後,自動完成清理工作,無需手動干預。
2、在需要管理一些資源比如檔案、網路連線和鎖的程式設計環境中,可以在__exit__中制定自動釋放資源的機制,你無須再關注這個問題,有很大好處。
十七、元類
python中一切皆物件,類本身也是物件,當使用關鍵字class的時候,python直譯器在載入class的時候就會建立一個物件(這裡的物件是類而非類的例項)
元類是類的類,是類的模板
元類是用來控制如何建立類的,正如類是建立物件的模板一樣
元類的例項為類,正如類的例項是物件(f物件是Foo類的例項,Foo類是type類的一個例項)
建立類的方式有兩種:
# _*_ encoding:utf-8 _*_ __author__ = 'listen' __date__ = '2018/12/8 11:19' #python中一切皆物件 類也是物件 #物件的類是類 類類是元類 type
#第一種 class Foo: def __init__(self,name,age): self.name=name self.age=age f=Foo('alex',18) # print(Foo) #<class '__main__.Foo'> 這個類的類名 print(f.__class__) #<class '__main__.Foo'> print(Foo.__class__) #<class 'type'> # #例項化 # def __init__(self,name,age): # self.name=name # self.age=age # def test(self): # print('=====') # 第二種 FFoo=type('FFoo',(object,),{'x':1,'__init__':__init__,'test':test}) #字典中放資料屬性 # f1=FFoo('listen',23) # print(f1.name,f1.age) #listen 23 實現元類的例項化呼叫 # f1.test() #=====
自定義元類,實現類的自定義
相關推薦
**python面向物件進階
isinstance(obj,cls)和issubclass(sub,super) #isinstance(obj,cls)檢查是否obj是否是類 cls 的物件 class Foo(object): pass obj = Foo() isinstance(obj, Foo
Python--面向物件進階
isinstance和issubclass isinstance isinstance(obj1,obj2):判斷物件與類之間的關係,判斷第一個引數是否是第二個引數的例項。 >>> n1 = 10 >>> isinstance(n1, int) #判斷n1是否
python-面向物件進階
python-面向物件進階 三大特性:繼承,多型,封裝 1,初識繼承 繼承指的是類與類之間的關係,是一種什麼“是”什麼的關係,繼承的功能之一就是用來解決程式碼重用問題。 繼承是一種建立新類的方式,在python中,新建的類可以繼承一個或多個父類,父類又可以成為基類或超類,新建的類稱
python-----面向物件進階
一、反射 概念:主要是指程式可以訪問、檢測和修改它本身狀態或者行為的一種能力(自省) python面向物件中的反射:通過字串的形式操作物件的相關屬性。python中的一切事物皆物件(都可以使用反射) 一、四個可以實現自省的函式: hasattr(obj,'屬性名') 檢測物件是否含有某屬性
Python面向物件進階
二分 二分的前提是有序 時間複雜度為O(log n) bisect模組 bisect系,用於查詢index bisect.bisect_left bisect.bisect_right insort系,用於實際插入 bisect.insort_le
Python -- 面向物件進階之--生成器
生成器 1. 什麼是生成器 通過列表生成式,我們可以直接建立一個列表。但是,受到記憶體限制,列表容量肯定是有限的。而且,建立一個包含100萬個元素的列表,不僅佔用很大的儲存空間,如果我們僅僅需要訪問前面幾個元素,那後面絕大多數元素佔用的空間都浪費了。所以,如果列表元素可以
Python -- 面向物件進階之--迭代器
迭代器 迭代是訪問集合元素的一種方式。迭代器是一個可以記住遍歷的位置的物件。迭代器物件從集合的第一個元素開始訪問,直到所有的元素被訪問完結束。迭代器只能往前不會後退。 1. 可迭代物件 可以直接作用於for迴圈的資料型別有以下幾種: 一類是集合資料型別,如 list
Python -- 面向物件進階之--裝飾器
裝飾器 一、介紹: 裝飾器實際上就是為了給某程式增添功能,但該程式已經上線或已經被使用,那麼就不能大批量的修改原始碼,這樣是不科學的也是不現實的,因此就產生了裝飾器,使得其滿足: 不能修改被裝飾的函式的原始碼 不能修改被裝飾的函式的呼叫方式 滿足
週末班:Python基礎之面向物件進階
面向物件進階 型別判斷 issubclass 首先,我們先看issubclass() 這個內建函式可以幫我們判斷x類是否是y型別的子類。 class Base: pass class Foo(Base): pass class Bar(Foo):
python筆記(面向物件進階:內建函式)
1、雙下方法:str,repr repr(): print(1,'1') print(repr(1)) print(repr('1')) 1 1 1 ‘1’ (1)str class A: def __init__(self,name
python筆記(面向物件進階:反射)
一、反射:getattr,hasattr 1、getattr()和hasattr(): class Teacher: dic = {'檢視學生資訊':'','檢視講師資訊':''} def show_student(self):
python 入門第七課 面向物件進階
靜態方法: @staticmethod,只是名義上歸類管理,實際上在靜態方法裡訪問不了類或者例項的任何屬性 類方法: @classmethod,只能訪問類變數,不能訪問例項變數 屬性方法: @property ,把一個方法變成一個靜態屬性 屬性方法的應用如下: f.flight_status呼叫屬性方法,.s
python學習------面向物件進階
一 isinstance(obj,cls)和issubclass(sub,super) isinstance(obj,cls)檢查是否obj是否是類 cls 的物件 class Foo(object): pass obj = Foo() isinstance(ob
python教程12、面向物件進階
上一篇《面向物件基礎》文章介紹了面向物件基本知識: 面向物件是一種程式設計方式,此程式設計方式的實現是基於對 類 和 物件 的使用 類 是一個模板,模板中包裝了多個“函式”供使用(可以講多函式中公用的變數封裝到物件中) 物件,根據模板建立的例項(即:物件
python全棧開發-面向物件-進階2 python_day_19
今日主要內容: 1.抽象類,介面類 2.多型 3.封裝 1.抽象類,介面類 python 沒有介面這個概念 介面類,抽象類: 制定一個規範. 舉個栗子: 你的專案經理提一個需求,讓你做一個支付功能. 第一版: class Alipay: def __ini
Python之旅11:面向物件進階遍
本章內容: 面向物件三大特性(封裝、繼承、多型) 類的成員 一、面向物件三大特性 面向物件的三大特性是指:封裝、繼承和多型。 1、封裝 封裝,顧名思義就是將內容封裝到某個地方,以後再去呼叫被封裝在某處的內容。 所以,在使用面向物件的封裝特性時,需要: 將
Python基礎(13):面向物件進階(訪問限制,__slots__,property,獲取物件資訊,類屬性和例項屬性)
一,訪問限制 原因:直接操作物件屬性有兩個缺點:無法保證資料安全性,無法進行引數校驗。 示例: class fruit(object): #定義一個類 def __init__(self,name): #定義屬性name
Python筆記day28(物件)|面向物件進階、hashlib
1,內容回顧 # 作業 ——> 反射 # str 和 repr # 類 object # repr str # str --> 類 str repr 父類object str repr # str --> 類 str 父類str 類rep
Day7-Python3基礎-面向物件進階
內容: 面向物件高階語法部分異常處理 經典類vs新式類 靜態方法、類方法、屬性方法 類的特殊方法 反射 Socket開發基礎 面向物件高階語法部分 靜態方法 &
9.面向物件進階
一.isinstance(obj,cls)和issubclass(sub,super)(1)isinstance(obj,cls)檢查一個物件obj是否是一個類cls的例項(2)issubclass(sub,super)檢查sub類是否是super類的派生類 class Foo: