day26 面向物件的常用方法 和 元類的使用
阿新 • • 發佈:2018-12-19
1. 面向物件中的常用方法 *****
isinstance() # 判斷某個物件是不是某個類的例項
# 判斷stu物件是不是Student類的例項
是不是子類
issubclass()
class Person: pass class Student(Person): pass stu = Student() #判斷 兩個物件是不是同一個型別 print(type(1) == type(1)) # 判斷stu物件是不是Student類的例項 print(isinstance(stu,Student)) # 是不是子類 # issubclass() # 判斷一個類是不是 另一個類子類 (所有類都是object的子類或子子類) print(issubclass(Student,Person)) print(isinstance(type,type)) 不僅能判斷父類,還能判斷父類的父類
2. 反射 *****
反射 其實說的是反省
簡單的說就是物件具備一種修正錯誤的能力
hasattr 是否存在某個屬性
getattr 獲取某個屬性的值
setattr 設定某個屬性的值
delattr 刪除某個屬性
class Student:
def __init__(self,name,sex,age): self.name = name self.age = age self.sex = sex def study(self): print("學生正在學習...") stu = Student("矮根","woman",38) print(stu.name) stu.name = "高根" print(stu.name) del stu.name print(stu.name) # 當你獲取到一個物件 但是並不清楚搞物件的內部細節時 就需要使用反射了 def test(obj): if hasattr(obj,"name"): print(getattr(obj,"name","沒有name屬性")) test(stu) setattr(stu,"school","beijing") delattr(stu,"school") print(getattr(stu,"school","沒有學校屬性")) delattr(stu,"age") print(stu.age) 這幾個方法有一個共同點,都是通過字串來操作屬性 你可以理解為通過字串來操作屬性,就叫做屬性 如果在編寫程式碼期間 就能明確知道我要訪問的屬性 沒有必要使用反射 如果在編寫程式碼期間 無法明確知道我要訪問的屬性 這時就應該使用反射 # class Student: # def study(self): # print("學習中....") # stu = Student() # res = getattr(stu,"study",None) # print(res) # def eat(self): # print("正在吃飯...") # # 可以通過反射的方式為物件增加一個方法 但是注意 這樣增加的方法就是一個普通函式 不會自動傳值 # setattr(stu,"eat",eat) # print(getattr(stu,"eat",None)) # 需要編寫一個CMD工具 這個工具可以支援兩個命令 dir ,tasklist class CMD: def dir(self): print("列出當前資料夾目錄....") def tasklist(self): print("檢視任務列表.....") cmd = CMD() res = input("請輸入指令:").strip() if hasattr(cmd,res): func = getattr(cmd,res) print(func) func() else: print("輸入的指令不正確....")
str *****
__str__
前後帶槓槓的都是特殊的內建函式,會在某些時機自動執行,一般情況我們不應該直接呼叫他們
當我們需要自定義列印顯示內容時,就需要實現__str__方法
該方法返回一個字串 返回的是什麼,打印出來就是什麼
class Test: def __init__(self,name): self.name = name def __str__(self): print("str run....") return self.name t = Test("安米") print(int(1).__str__()) # print([1,2,3,5].__str__()) print(t) # 在講一個物件轉換字串時 本質就是在呼叫這個物件 __str__方法 print(str(t))
del ****
當物件被從記憶體中刪除時,自動執行
另一種情況是,程式設計師手動刪除了這個物件,也會自動執行
什麼時候使用它
在python中有自動記憶體管理機制,所有python自己建立的資料,不需要我們做任何操作
但是有一種情況,我們使用python打開了一個不屬於python管理的資料
比如打開了一個檔案,這個檔案一定是作業系統在開啟 會佔用系統記憶體 而python直譯器無法作業系統記憶體的
所以 當你的python直譯器執行結束後 檔案依然處於開啟狀態 這時候就需要使用__del__來關閉系統資源
__del__也稱之為解構函式
分析構造 並拆除這個物件
該方法其實就是一個通知性質,僅僅是告訴程式設計師,物件即將被刪除
分析構造並拆除這個物件
class Student: def __del__(self): print("物件被刪除了....") stu = Student() # 手動刪除 立即執行__del__ del stu import time time.sleep(5) class TextFile: def __init__(self,filepath,mode="rt",encoding="utf-8"): self.file = open(filepath,mode=mode,encoding=encoding) def read(self): return self.file.read() def write(self,text): self.file.write(text) # 該方法其實就是一個通知性質 僅僅是告訴程式設計師 物件即將被刪除 def __del__(self): # 在這裡關閉系統的檔案 妥妥的 self.file.close() tf = TextFile("2.今日內容.txt") print(tf.read()) # tf.file.close() # 不需要手動關閉了 在物件刪除時會自動關閉 tf.read()
exec ****
execute的縮寫
表示執行的意思
其作用 是幫你解析執行python程式碼 並且將得到的名稱 儲存到制定的名稱空間 直譯器內部也是呼叫它來執行程式碼的
引數一需要一個字串物件,表示需要被執行的python語句
引數二是一個字典,會把字典放在全域性名稱空間中
引數三也是一個字典,會把字典放在區域性名稱空間中
exec('')
globalsdic = {}
localsdic = {}
exec("""
aaaaaaaaaaaaaaaaaaaa = 1
bbbbbbbbbbbbbbbbbbbbbbbbbbbb = 2
def func1():
print("我是func1")
""",globalsdic,localsdic)
# 如果同時制定了 全域性和區域性 則 會字串中包含名稱 解析後存到區域性中
# print(globalsdic)
print(localsdic)
localsdic["func1"]() 在字串中定義好函式後,要用後面的字典名稱去呼叫函式,直接在全域性空間或者區域性名稱空間,用函式名呼叫會找不到
元類 ****
一切皆物件
元類是指用於產生類的類,type就是元類
所有的自定義類都是通過type例項化得來的
使用type可以發現,類其實是type型別的例項(物件)
建立模組的過程
1.建立一個空的名稱空間
2.執行其內部的程式碼
3.將得到的名字放到名稱空間中
# class也是一個物件
class Student(object): school = "北京大學!" def study(self): print("學習中...") # 使用type可以發現 類其實是type型別的例項(物件) print(type(Student)) 我們可以自己呼叫type來例項化產生一個類 # myclass 包含的程式碼 code = """ name = "張三" age = 18 def hello(self): print("hello %s" % self.name) """ #類的名字 class_name = "MyClass" #類的的父類們 base_classes = (object,) #類的名稱空間 namespace = {} exec(code,{},namespace) res = type(class_name,base_classes,namespace) print(Student) print(res.name) print(res.age) print(res.hello) 1.類是由type例項化產生的 2.我們可以使用type來產生一個類 3.一個類由類名字,類的父類元組,類的名稱空間三個部分組成 class Test(object): #就相當於Test = type("Test",(object,),{}) pass
自定義元類,元類也是一個類
def Person(metaclass = MyMeta)
call
呼叫的意思
在物件被呼叫(加括號時)時,執行
函式 類
自定義元類的目的
1.可以通過__call__來控制物件的建立過程
2.可以控制類的建立過程
class MyMeta(type): def __call__(self,*args,**kwargs): print('from Mymeta call run') print(self,*args,*kwargs) #下面三步是建立物件的固定寫法,一個模板,只有你需要控制物件的建立過程,就必須先把模板寫出來 obj = object.__new__(self) self.__init__(obj,*args,**kwargs) return obj class Person(metaclass=Mymeta): def __init(self,name,age): self.name = name self.age = age def __call__(self,*args,**kwargs): print('Peson call run...') #呼叫Person這個物件時 執行的是 Person的類(type)中__call__ 方法 p = Person("張三瘋",80) print(p) # 當呼叫物件時 會執行該物件所屬類中的__call__方法 p() print(p.name) print(p.age) # 要控制類的建立過程 只要找到類所屬的類 中的__init__即可 class MyMeta(type): #要自己完成__init__函式完成類的建立 #self 表示要建出來的類 #第二個引數 類的名字 #第三個引數 類的父類們,元組形式 #第四個引數 這個類傳進來的名稱空間 def __init__(self,class_name,bases,namespace): # (在--init__中不實現賦值操作,python也會自動按位置傳參) print('==========') #print(self.__dict__) # 我要控制 類的名字 必須 是大寫開頭 if not class_name.istitle(): print("類名 必須大寫開頭...... ") # 該程式碼是主動丟擲異常 raise TypeError("類名 必須大寫開頭...... ") if not self.__doc__: raise TypeError('類中必須有中文件註釋') pass class Student(metaclass=MyMeta): # Student = MyMeta("student",(object,),{}) ''' 這是文件註釋,可以通過__doc__來獲取 這是一個學生類 ''' # 在類的__init__中可以控制該類物件的建立過程 def __init__(self,name): print('=======') print(self.__dict__) self.name = name print(Student.__doc__)
元類總結
元類是用於建立類的類
學習元類是為了能控制類的建立過程以及類例項化物件的過程
一.
控制類的建立過程
1.建立一個元類(需要繼承type)
2.覆蓋__init__方法 該方法 會將新建的類物件 類名 父類們 名稱空間 都傳進來,可以用這些資訊再做處理
3.對於需要被控制的類 需要指定metaclass為上面的元類
二.
控制類例項化物件的過程
1.建立一個元類(需要繼承type)
2.覆蓋__call__方法 會將正在例項化物件的類,呼叫類時傳入的引數,都傳進來 3.在__call__方法中,必須要先編寫模板程式碼 3.1建立空物件 3.2呼叫類的__init__方法來初始化這個空物件 3.3返回該物件 4.加入你需要控制的邏輯 類的三個組成部分 類名 父類名 名稱空間 元類-> 例項化產生-> 類 -> 例項化產生 -> 物件
單例模式 ****
一種設計模式(套路)
單個例項
一個類如果只有一個例項,那麼該類稱為單例
為什麼需要單例
class MyMeta(type):
obj = None def __init__(self,*args,**kwargs): if not MyMeta.obj: obj = object.__new__(self) self.__init__(obj,*args,**kwargs) MyMeta.obj = obj return MyMeta.obj 印表機類 class Printer(metaclass=MyMeta): ''' 這是一個單例類 請不要直接例項化,使用get方法來獲取例項 ''' obj = None def __init__(self,name,brand,type): self.name = name self.brand = brand self.type = type def printing(self,text): print("正在列印 %s" % text) # 通過該方法來獲取物件 可以保證只有一個物件 # 但是這還不夠 因為 還是可以通過呼叫類產生新物件 # 就應該使用元類 來控制例項化的過程 __call__ # 在__call__ 中編寫程式碼 保證每次呼叫call 都返回同一個例項 即可 @classmethod def get_printer(cls): if not cls.obj: obj = cls("ES005","愛普生","彩色印表機") cls.obj = obj print("建立了新的物件") return cls.obj p = Printer.get_printer() print(p) p = Printer.get_printer() print(p) p = Printer.get_printer() print(p) p = Printer.get_printer() print(p) p1 = Printer("ES005","愛普生","彩色印表機") p2 = Printer("ES005","愛普生","彩色印表機") print(p1) print(p2) # print(p1,p2,p3) # p1.printing("一本小說....")