一、補充內置函數isinstance和issubclass
1、isinstance是判斷一個對象是不是由一個對象產生的
1 class Foo: 2 pass 3 4 obj=Foo() 5 6 print(isinstance(obj,Foo)) #判斷一個對象是否是由某個類調用產生 7 8 # 在python3中統一類與類型的概念 9 d={'x':1} #d=dict({'x':1} #) #類即類型,d是一個對象,dict是一個類,d是由調用dict產生的對象 10 11 print(type(d) is dict) #type也可以判斷數據類型,但這並不是type的的主要功能,其實type是一個元類 12 print(isinstance(d,dict))
2、issubclass是判斷一個l類是不是另外一個類的子類
# issubclass() class Parent: pass class Sub(Parent): pass print(issubclass(Sub,Parent)) #判斷類Sub是不是Parent是不是的子類,是布爾值為True print(issubclass(Parent,object))
二、反射
''' 1、什么是反射 通過字符串來操作類或者對象的屬性 2、如何用 hasattr getattr setattr delattr ''' class People: country='China' def __init__(self,name): self.name=name def eat(self): print('%s is eating' %self.name) peo1=People('egon') # hasattr:判斷-------(對象)---------中有沒有一個------(字符串形式的屬性)-------對應的數據屬性或函數屬性,結果是一個bool值 print(hasattr(peo1,'eat')) #效果等價:peo1.eat # # getattr:判斷-------(對象)---------中有沒有一個------(字符串形式的屬性)-------對應的數據屬性或函數屬性,如果有對應的數據屬性,返回的是數據屬性對應的值,如果有對應的函數屬性返回的是一個內存地址 print(getattr(peo1,'eat')) #peo1.eat #類中有這個屬性,返回的是一個內存地址 print(getattr(People,'country')) #類中有數據屬性,返回的是數據屬性對應的屬性值 print(getattr(peo1,'xxxxx',None)) #None是一個默認值,如果類中沒有這個數據屬性、函數屬性就會將默認值返回,如果沒有默認值,類中也沒有字符串對應的屬性,就會報錯 # setattr:判斷-------(對象)---------中有沒有一個------(字符串)-------對應的數據屬性,有可以對其屬性值進行修改,沒有添加新的屬性 setattr(peo1,'age',18) #peo1.age=18 #可以給對象添加新的屬性 setattr(peo1,'name','alex') #可以修改對象的屬性值 print(peo1.age) print(peo1.name) # print(peo1.__dict__) # delattr:判斷-------(對象)---------中有沒有一個------(字符串形式的屬性)-------對應的數據屬性或函數屬性,有可以將其刪除 delattr(peo1,'name') #del peo1.name print(peo1.__dict__) delattr(peo1,'eat') #-----會報錯,setattr和delattr只會對自己名稱空間的名字做修改和刪除,並不能改變類名稱空間中的屬性名 '''重點''' # 反射的應用: class Ftp: def __init__(self,ip,port): self.ip=ip self.port=port def get(self): print('GET function') def put(self): print('PUT function') def run(self): while True: choice=input('>>>: ').strip() #模擬用戶實際輸入對應的命令是一個字符串,所以此時就要用到映射 '''方法一''' # # print(choice,type(choice)) # if hasattr(self,choice): #判斷用戶輸入的字符串形式的屬性名是否存在,拿到一個返回的bool值 # method=getattr(self,choice) #輸入的字符串形式的屬性名存在,通過屬性名拿到綁定方法的內存地址 # method() #綁定方法加括號即調用 # else: # print('輸入的命令不存在') '''方法二''' method=getattr(self,choice,None) #如果用戶輸入的字符串形式的屬性值存在,則直接拿到該屬性的綁定方法的內存地址返回,屬性是不存在,返回None if method is None: print('輸入的命令不存在') else: method() #函數屬性的內存地址加括號直接調用 conn=Ftp('1.1.1.1',23) #類加括號,產生一個空對象,並對對象通過__init__進行初始化 conn.run() #將對象綁定給方法,會將對象當做第一個參數自動傳入
三、自定義方法來定義類的功能
1、__str__方法-------會在打印對象的時候自動觸發
class People: def __init__(self,name,age): self.name=name self.age=age #在對象被打印時,自動觸發,應該在該方法內采集與對象self有關的信息,然后拼成字符串返回 def __str__(self): # print('======>') return '<name:%s age:%s>' %(self.name,self.age) #返回的必須是字符串,否則就會報錯 obj=People('egon',18) obj1=People('alex',18) print(obj) #obj.__str__() #沒有__str__打印對象的結果就是一個內存地址, # 但是有了__str__我們就可以拼接任意我們想要的樣式,此時在打印對象就會得到我們拼接的結果 print(obj) #obj.__str__() print(obj) #obj.__str__() print(obj1) #obj1.__str__() """ 默認就有str,只是返回的是內存地址,我們可以通過設置str,可以通過打印對象打印出好看的格式,而不是只是單純的打印出一個內存地址""" d={'x':1} #d=dict({'x':1}) print(d) #打印d這個對象是一個字典,而不是像上面一樣是一個內存地址,是因為dict內一定自帶了一個__str__,在打印對象的時候自動觸發
2、__del__會在對象被刪除時自動觸發
# """1、__del__析構方法""" # # __del__會在對象被刪除時自動觸發 class People: def __init__(self,name,age): self.name=name self.age=age self.f=open('a.txt','rt',encoding='utf-8') #打開該文件占用兩方面的資源,一是應用程序即Python解釋器,Python會自動回收,另一方面是系統資源,要手動關閉 def __del__(self): #刪除對象是自動觸發 print('run=-====>') # 做回收系統資源相關的事情 self.f.close() #刪除應用程序資源要在關閉系統資源之后,要不就會出現系統資源無法關閉,白白占用資源 obj=People('egon',18) print('主') #系統程序運行完畢,回收資源,會自動觸發__del__的執行,所以先打印:'主',再打印:'run=-====>'
四、元類
''' 1、什么是元類 在python中一切皆對象,那么我們用class關鍵字定義的類本身也是一個對象 負責產生該對象的類稱之為元類,即元類可以簡稱為類的類 class Foo: # Foo=元類() #一切皆對象,類加括號產生對象 pass 2、為何要用元類 元類是負責產生類的,所以我們學習元類或者自定義元類的目的 是為了控制類的產生過程,還可以控制對象的產生過程 3、如何用元類 ''' #1、儲備知識:內置函數exec的用法 cmd=""" x=1 def func(self): pass """ class_dic={} exec(cmd,{},class_dic) #exec會將cmd字符串中的代碼拿出來執行一次,將產生的名字丟掉事先定義好的class_dic空字典中 print(class_dic) #{'x': 1, 'func': <function func at 0x00000267165F92F0>} #2、創建類的方法有兩種 # 大前提:如果說類也是對象的話,那么用class關鍵字去創建類的過程也是一個實例化的過程 # 該實例化的目的是為了得到一個類,調用的是元類 #2.1 方式一:用的默認的元類type class People: #People=type(...)--------默認的元類type實例化出一個對象Pelple,實例化的結果也是一個對象 country='China' def __init__(self,name,age): self.name=name self.age=age def eat(self): print('%s is eating' %self.name) peo=People('EGON',18) print(peo) #------------<__main__.People object at 0x000001F635282E10>*********調用類實例化出對象 print(type(People)) #------------<class 'type'>*****************************************調用元類實例化出類 """重點""" #2.1.1 創建類的3個要素:類名,基類,類的名稱空間 class_name='People' #類名,是一個字符串,---------由上面的class定義類我們知道,創建類的三要素:類名,基類,類的名稱空間 class_bases=(object,) #基類,----------------------我們通過__bases__,知道基類是一個元組的形式 class_dic={} #類的名稱空間,---------------通過__dict__,知道類的名稱空間的是一個字典 class_body=""" country='China' def __init__(self,name,age): self.name=name self.age=age def eat(self): print('%s is eating' %self.name) """ #--------將類體代碼放到一個字符串中 exec(class_body,{},class_dic)#執行字符傳中的代碼,將產生的名字方到class_dic的名稱空間中,即之前定義類將產生的名字放到類的名稱空間中 # 准備好創建類的三要素 # print(class_name) #-------People # print(class_bases) #-------(<class 'object'>,) # print(class_dic) #-------{'country': 'China', '__init__': <function __init__ at 0x00000222D55F92F0>, 'eat': <function eat at 0x00000222DC618BF8>} # People=type(類名,基類,類的名稱空間) #調用元類就可以產生一個類這個對象 People1=type(class_name,class_bases,class_dic) #將事先定義好的類的三要素放到當做參數傳給元類,調用元類即產生對象 print(People1) #--------<class '__main__.People'>自定義類產生的結果 obj1=People1('egon',18) print(People) #--------<class '__main__.People'>,class定義類產生的結果 obj=People('egon',18) obj1.eat() obj.eat() """----------------------------------------重點----------------------------------------""" #2.2 方式二:用的自定義的元類 class Mymeta(type): #只有繼承了type類才能稱之為一個元類,否則就是一個普通的自定義類 def __init__(self,class_name,class_bases,class_dic): print(self) #現在是People print(class_name) print(class_bases) print(class_dic) super(Mymeta,self).__init__(class_name,class_bases,class_dic) #重用父類的功能 # 分析用class自定義類的運行原理(而非元類的的運行原理): #1、拿到一個字符串格式的類名class_name='People' #2、拿到一個類的基類們class_bases=(obejct,) #3、執行類體代碼,拿到一個類的名稱空間class_dic={...}------------------前三步就是造類的三要素 #4、調用People=type(class_name,class_bases,class_dic)----------------調用元類(類)產生類(對象)------------調用類產生對象 class People(object,metaclass=Mymeta): #People=Mymeta(類名,基類們,類的名稱空間)------metaclass=Mymeta是自定義的元類名 country='China' def __init__(self,name,age): self.name=name self.age=age def eat(self): print('%s is eating' %self.name) """----------------------------------------重點----------------------------------------""" """應用:自定義元類控制類的產生過程,類的產生過程其實就是元類的調用過程------(對象的產生過程就是調用類的過程)""" class Mymeta(type): #只有繼承了type類才能稱之為一個元類,否則就是一個普通的自定義類---------------必須要繼承type類 def __init__(self,class_name,class_bases,class_dic): #在自定義類之上添加邏輯判斷 if class_dic.get('__doc__') is None or len(class_dic.get('__doc__').strip()) == 0: #必須有文檔注釋,且不為空 raise TypeError('類中必須有文檔注釋,並且文檔注釋不能為空') if not class_name.istitle(): #類的首字母必須大寫 raise TypeError('類名首字母必須大寫') super(Mymeta,self).__init__(class_name,class_bases,class_dic) #重用父類的功能 class People(object,metaclass=Mymeta): #People=Mymeta('People',(object,),{....}) """這是People類""" country='China' def __init__(self,name,age): self.name=name self.age=age def eat(self): print('%s is eating' %self.name) #3 儲備知識:__call__ class Foo: def __call__(self, *args, **kwargs): print(self) #<__main__.Foo object at 0x000002193E892E10> print(args) #(1, 2, 3)----------------*args接收位置參數,存成元組的形式 print(kwargs) #{'x': 1, 'y': 2}---------**kwargs接收關鍵字參數,存成字典的形式 obj=Foo() #調用類不會自動觸發,會在調用對象時自動觸發,通過self也可以看出,是調用對象時自動觸發 # # 要想讓obj這個對象變成一個可調用的對象,需要在該對象的類中定義一個方法__call__方法 # # 該方法會在調用對象時自動觸發 # obj(1,2,3,x=1,y=2) #調用對象時自動觸發__call__方法,並將對象自動傳入 """-----------------------------------------------重點---------------------------------------------""" # 4、自定義元類來控制類的調用的過程,即類的實例化過程 class Mymeta(type): def __call__(self, *args, **kwargs): #會在調用對象時自動觸發,此時的對象時一個類,即People # print(self) # self是People # print(args) # print(kwargs) # return 123 """調用類產生一個對象,發生兩件事""" #和class定義類,調用類一樣發生兩件事 # 1、先造出一個People的空對象 obj=self.__new__(self) #造出了一個自定義類People的空對象 # 2、為該對空對象初始化獨有的屬性 # print(args,kwargs) self.__init__(obj,*args,**kwargs) #對空對象進行初始化,空對象傳入,以及參數原封不動的傳入 # 3、返回一個初始好的對象 return obj #將造出的對象返回, '''**********************************看成一個對象************************************************''' class People(object,metaclass=Mymeta): #自定義類People,元類是Mymeta,元類必須繼承type類,否則就不是元類 country='China' def __init__(self,name,age): self.name=name self.age=age def eat(self): print('%s is eating' %self.name) def __new__(cls, *args, **kwargs): #對象自己中有__new__屬性,先從對象自己的名稱空間中找,自己沒有在到自己的類中找 print(cls) # cls.__new__(cls) # 錯誤 #自己有調用了自己的__new__,這樣就出現無線遞歸,所以會報錯 obj=super(People,cls).__new__(cls) #自己中有,我們任然讓其取繼承父類中的__new__屬性,來產生一個空對象,然后將對象初始化,拿到一個返回值 return obj '''**********************************看成一個對象************************************************''' """-----------------------------------------------重點---------------------------------------------""" # 分析:調用Pepole的目的 #1、先造出一個People的空對象 #2、為該對空對象初始化獨有的屬性 # obj1=People('egon1',age=18) # obj2=People('egon2',age=18) # print(obj1) # print(obj2) # obj=People('egon',age=18) # print(obj.__dict__) # print(obj.name) # obj.eat()