今日內容:
1.元類
2.異常
Python:一門動態語言
最大特點:運行期間動態生成類,修改對象屬性
詳解:
1.元類:
1.1什麼是元類?
一切皆對象
類也是對象,可以把一個類當作普通對象對象來使用,(對象怎麼用,類就怎麼用)
使用:
例如,存儲到列表中,或者作為參數傳給函數等等
對象是如何產生的?
通過實例化產生的
類對象:
是由type實例化產生的
案例:
class Aclass:
pass
print(type(Aclass))
#值<class 'type'>
我們可以手動調用type來實例化產生一個類
一個類由三部分組成:
1.類的名稱 我是誰
2.類的父類們 我從哪裡來
3.類的名稱空間 我有什麼
type的兩種使用方法:
.type(類名,父類元祖,名稱空間字典)#返回一個新的類
type(對象) #將會返回這個對象的類型
所以:我們可以總結出 當你定義一個class時,解釋器會自動調用type來完成類的實例化
案例:
#模擬解釋器創建類的對象
def test1(a):
print(a)
def test2(self,b):
print(self,b)
class_name="c"
bases=(object,)
name_dict={"name":"jack","test1":test1,"test2":test2}
c=type(class_name,bases,name_dict)
print(c)
#值:產生一個新的類<class '__main__.c'>
c1=c()
print(c1)
#值:<__main__.c object at 0x00000256F4337A90>
c1.test2(100)
#值:<__main__.c object at 0x000002405D6A7A90> 100
1.2補充:解釋器執行代碼的時候就是exec
exec(執行)與eval(評價)
glob全局名稱空間 locl局部名稱空間
exec用於執行字符串形式的Python代碼 只要符合Python都能執行,並且可以指定將執行產生的名字放入某個名稱空間
eval用於執行簡單的表達式,(加減,基礎運算)不能有任何的特殊語法
class_test="""
class A:
def test(self):
print(self)
"""#字符串形式的Python代碼
loca2={}
exec(class_test,None,loca2)
#print(loca2)
#值{'A': <class '__main__.A'>}
eval(class_test)#語法錯誤
1.3元類(Type)(元類繼承自object):用於產生類的類,稱之為元類
元類翻譯為:
metaclass 只要看見他就應該想起來這就是元類
我們在定義元類的時候,儘量在類名後添加MetaClass 方便閱讀
1.4(應用)用來幹啥?(三種方法:__init__,__new__,__call__)
當我們需要高度定製類的時候,如限制類名必須大寫開頭等等。。。。
就需要使用元類,但是元類type中的代碼 無法被修改,只能創建新的元類(繼承自type)通過覆蓋__init__來完成對類的限制
使用元類
如何自定義元類:
class MyMetaCLass(type):
pass
#使用自定義元類
class Person(metaclass=MyMetaCLass):
pass
1.4.1__init__方法(重點)
實例化對象時會自動執行類中__init__方法,類也是對象,在實例化對象時會自動執行元類中__init__方法
並且傳入類的三個必要參數,類的名字,父類們,名稱空間
當然會自動傳入類對象本身作為第一個參數
案例:說明
限制類名必須首字母大寫 控制類中的方法名 必須全部小寫
class MyMetaClass(type):
def __init__(self,class_name,bases,name_dict):
super().__init__(class_name,bases,name_dict)
#類名必須首字母大寫,否則直接剖出異常
if not class_name.istitle():
print("類名必須大寫")
raise Exception
#控制類中的方法名必須全部小寫
for k in name_dict:
if str(type(name_dict[k]))=="<class 'function'>":
if not k.islower():
raise Exception
pass
#會自動調用其他元類中 __init__方法傳入 類對象本身 類名稱 父類們 名稱空間
class Student(object,metaclass=MyMetaClass):#MyMetaClass("student",(object,),{})
NAME=10
def say(self):
pass
1.4.2__new__方法(重點)
元類中的new方法會在創建類對象時執行,並且先於init方法
作用是創建一個類對象
class A(metaclass=MyMetaClass):
pass
1.執行MyMetaClass的__new__
方法 拿到一個類對象
2.執行MyMetaClass的__init__
方法 傳入類對象以及其他的屬性 ,進行初始化
注意:如果覆蓋了__new__
一定也要調用type中的__new__
並返回執行結果
在調用init方法前類對象已經創建完成了
所以如果對性能要求高的話 可以選在在new中完成定製 如果發現有問題,就不用創建類對象了
需求: 要求每個類必須包含__doc__
屬性(列印注釋)
class DocMeatClass(type):
def __init__(self,class_name,bases,name_dict):
super().__init__(class_name,bases,name_dict)
練習:
# 需求: 要求每個類必須包含__doc__屬性 __doc__ 用於訪問一個對象的注釋信息
# class A:
# """
# this is a Class
# author is jerry
# """
# pass
# print(A.__doc__)
1.4.3__call__方法(重點)
元類中的 call方法會在調用類時執行,
可以用於控制對象的創建過程
class MyMeta(type):
總結:當你要定製類時,就自定義元類並覆蓋init方法
1.5元類實現單例模式
什麼是單例:
某個類如果只有一個實例對象,那麼該類成為單例類
單例的好處:
當某個類的所有對象特徵和行為完全一樣時,避免重複創建對象,浪費資源
案例:
class SingletonMetaClass(type):
#創建類時會執init 在這為每個類設置一個obj屬性 默認為None
def __init__(self,a,b,c):
super().__init__(a,b,c)
self.obj = None
# 當類要創建對象時會執行 該方法
def __call__(self, *args, **kwargs):
# 判斷這個類 如果已經有實例了就直接返回 從而實現單例
if self.obj:
return self.obj
# 沒有則創建新的實例並保存到類中
obj = type.__call__(self,*args,**kwargs)
self.obj = obj
return obj
2.異常
詳解
異常是程序運行過程中發生的非正常情況,是一個錯誤發生時的信號
異常如果沒有被正確處理的話,將導致程序被終止,這對於用戶體驗是非常差的,可能導致嚴重的後果
處理異常的目的就是提高程序的健壯性
python解釋器在執行代碼前會先檢查語法,語法檢查通過才會開始執行代碼
1.語法檢測異常 作為一個合格的程式設計師 是不應該出現這種低級錯誤
2.運行時異常
已經通過語法檢測,開始執行代碼,執行過程中發生異常 稱之為運行時異常
TypeError: 'int' object is not subscriptable 對象不能被切片
TypeError: 'list' object is not callable 對象不能被調用
IndexError: list index out of range 索引超出範圍
TypeError: 'builtin_function_or_method' object is not iterable 對象不能被迭代
KeyError: 'xxx' 不存在這個key
FileNotFoundError: [Errno 2] No such file or directory: 'xxxxx' 文件找不到
Traceback (most recent call last):
File "F:/python8期/課堂內容/day29/11.常見異常.py", line 22, in <module>
with open("xxxxx") as f:
FileNotFoundError: [Errno 2] No such file or directory: 'xxxxx'
Traceback 是異常追蹤信息 用於展示錯誤發生的具體位置 以及調用的過程
其中 包括了 錯誤發生的模塊 文件路徑 行號 函數名稱 具體的代碼
最後一行 前面是錯誤的類型
後面 錯誤的詳細信息 在查找錯誤時 主要參考的就是詳細信息
異常發生後 如果不正確處理將導致程序終止,我們必須應該儘量的避免這種情況發生
必須掌握的語法
語法:
try:
可能會出現異常的代碼 放到try裡面
except 具體異常類型 as e:
如果真的發生異常就執行except
當發生異常 不是立馬加try 要先找出錯誤原因並解決它
try 僅在 即使你知道為什麼發生錯誤 ,但是你卻無法避免
例如 你明確告訴用戶 需要一個正確文件路徑 然而用戶依然傳入了錯誤的路徑
如 socket 雙方都要使用管道 ,但是如果一方有由於某些原因強行關閉了 ,即使你知道原因也無法避免出錯 那就只能try 保證程序正常結束
總結一句話:能不加try 就不加try
當系統提供異常類不能準確描述錯誤原因時 就可以自定義異常類
繼承自Exception即可
class MyException(Exception):
pass
什麼時候需要主動拋出異常
當我們做功能的提供者,給外界提供一個功能接口
但是使用者不按照相應的方式來使用,或者參數類型不正確等原因,導致功能無法正常執行時,就應該主動拋出異常
主動拋出異常使用raise 關鍵字
後面可以跟任何Exception的子類 或是 對象
raise MyException
raise MyException("錯誤具體原因!")
斷言 其實可以理解為斷定的意思
即非常肯定某個條件是成立的
條件是否成立其實可以使用if來判斷
其存在的目的就是 為了簡化if 判斷而生的
優先掌握的內容:
1.元類中的三個方法的執行時機以及使用場景
2.單例模式
3.異常處理語法
4.要注重提升解決bug能力
有什麼用
如何處理
什麼是元類
有什麼用?
1.控制類的創建過程
2.控制對象的實例化過程
如何控制
控制類的創建過程 使用__init
控制對象的實例化過程 使用 call 也需要調用type的call 並返回其結果
__new 會先於__init執行 在__new中必須 調用type的__new 並返回結果