29 元类 异常
什么是元類
一切皆對象?
類也是對象,可以把一個類當(dāng)成普通對象來使用,比如存儲到列表中,或者作為參數(shù)傳給函數(shù)等等...
對象? ?通過類實(shí)例化產(chǎn)生的
類對象 是由type實(shí)例化產(chǎn)生的
例子
class A :
pass
print(type(A))
?
?
?
調(diào)用Ttye 實(shí)例化產(chǎn)生一個類
一個類由三個部分產(chǎn)生
類名字
類的父類們
名稱空間
?
type(類名,父類元組,名稱空間字典) #返回一個新的類
type(對象) #將會返回這個對象的類型
?
當(dāng)你定義一個class時,解釋器會自動調(diào)用type來完成類的實(shí)例化
?
?
動態(tài)語言可以在運(yùn)行期間 動態(tài)生成類
修改對象屬性
靜態(tài)語言
數(shù)據(jù)類型的檢查是在運(yùn)行前(如編譯階段)做的
方便調(diào)試,代碼相對規(guī)范
eg:
模擬解釋器創(chuàng)建類對象
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)
c1 = C()
print(c1)
c1.test2(100)
1.
exec 可以執(zhí)行字符串形式的python代碼 并且會把執(zhí)行過程中產(chǎn)生的名字 放到局部名稱空間中
glob = {}local = {}
code = """
def test(a):
print(a)
"""
exec(code, glob, local)
# print(glob)
print(local)
local['test'](10)
2.
eval 用于執(zhí)行簡單的表達(dá)式,不能有任何的特殊語法class_text = """
class A:
def test(self):
print(self)
"""
loca2 = {}
exec(class_text, None, loca2)
print(loca2)
# eval(class_text) 報錯 用于簡單的表達(dá)式
print(globals())
a = 100
def qq():
name = 11
元類
用于產(chǎn)生類的類?
定義元類時,盡量在類名后添加
MetaClass? ?方便閱讀
當(dāng)我們需要高度定制類時,如限制類名必須大寫開頭等等...
就需要使用元類,但是元類type中的代碼 無法被修改 ,只能創(chuàng)建新的元類(繼承自type) 通過覆蓋`__init__`來完成對類的限制?
class person():def __init__(self, name):
self.name = name
def SAYHL(self):
print(self.name)
type類已經(jīng)具備了創(chuàng)建類的能力
,但是現(xiàn)在不能滿足我們的需求需要對已有的功能進(jìn)行擴(kuò)展或修改
方式
1.直接修改源代碼 行不通
2.自己定義新的元類
class MyMetaClass(type):
pass
# 使用自定義元類
class Person(metaclass=MyMetaClass):
pass
實(shí)例化對象時會自動執(zhí)行類中的`__init__`方法, 類也是對象 ,在實(shí)例化類對象時會自動執(zhí)元類中的`__init__`方法? ? ?
傳入類的三個必要參數(shù),類的名字,父類們,名稱空間
會自動傳入類對象本身作為第一個參數(shù)
def __init__方法(類對象本身 類名 父類們 名稱空間): pass
自動調(diào)用其元類中的__init__方法傳入
eg: class MyMetaClass(type):
pass
# 默認(rèn)創(chuàng)建類時 是找的type來實(shí)例化的
class People(metaclass=MyMetaClass):
pass
class Student:
def __init__(self):
pass
print(type(People))
print(type(Student))
s = Student()
# 實(shí)例化對象時 ,1.產(chǎn)生空對象 2.自動執(zhí)行__init_
People = MyMetaClass()
# 創(chuàng)建類對象時也是一樣的 會先創(chuàng)建空的類對象 再調(diào)用__init__()方法 # 創(chuàng)建類時
class MyMetaClass(type):
# 類的實(shí)例化
def __init__(self, class_name, bases, name_dict):
"""
元類的應(yīng)用
self:表示的是類對象
:param class_name:類名
:param bases:父類們
:param name_dict:名稱空間
"""
# 調(diào)用父類的初始化
super().__init__(class_name, bases, name_dict)
print(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():
print('方法名全部小寫')
raise Exception
pass
# 正確寫入
class Students(object, metaclass=MyMetaClass):
NAME = 10
def say(self):
print('SAY')
pass
# 當(dāng) 類名 或 方法 大寫或小寫時 會拋出異常
__new__方法
元類中的new方法會在創(chuàng)建類對象時執(zhí)行,并且優(yōu)先于init方法
作用是創(chuàng)建一個類對象
class A(metaclass=MyMetaClass):
? pass
1.執(zhí)行MyMetaClass的`__new__`方法 拿到一個類對象
2.執(zhí)行MyMetaClass的`__init__` 方法 傳入類對象以及其他的屬性 ,進(jìn)行初始化
?
注意:如果覆蓋了`__new__` 一定也要調(diào)用type中的`__new__`并返回執(zhí)行結(jié)果
##### 使用new方法也可以完成定制類的工作 和init有什么區(qū)別?
在調(diào)用init方法前類對象已經(jīng)創(chuàng)建完成了
所以如果對性能要求高的話 可以選在new中完成定制 如果發(fā)現(xiàn)有問題,就不用創(chuàng)建類對象了
class MyClass(type):def __init__(self, class_name, bases, name_dict):
# 調(diào)用父類的初始化
# super().__init__(class_name, bases , name_dict)
print('init')
pass
# __new__()方法
# def __new__ 創(chuàng)建類對象
# 該方法會在實(shí)例化類對象時自動調(diào)用并且在 init 之前調(diào)用
# 其作用時用于創(chuàng)建新的類對象的
# 注意這里必須調(diào)用type類中的__new__ 否則將無法產(chǎn)生類對象 并且返回其結(jié)果
def __new__(cls, *args,**kwargs):
"""
cls 表示元類自己 即MyMetaClass
return 結(jié)果
"""
return type.__new__(cls,*args,**kwargs)
# 如果覆蓋__new__ 一定要寫上這行代碼 并返回
class Peopel(metaclass=MyClass):
pass
print(Peopel)
# 就算__init__中什么都不寫 這個類對象其實(shí)已經(jīng)創(chuàng)建完成了 該有的屬性都有了
# 這是與普通類不同之處
print(Peopel.__name__)
練習(xí) # 需求: 要求每個類必須包含__doc__屬性 自定義元類 覆蓋
__doc__ 用于訪問一個對象的注釋信息
class A:
"""
qwertyu
asdfgjhk
"""
pass
print(A.__doc__)
# 你要控制類的創(chuàng)建 那就自定義元類 覆蓋__init__
class DocMeatClass(type):
def __init__(self,class_name,bases,name_dict):
super().__init__(class_name,bases,name_dict)
if not self.__doc__:
raise Exception print(type(object))
`__call__`方法(重點(diǎn))
元類中的 call方法會在調(diào)用類時執(zhí)行,
可以用于控制對象的創(chuàng)建過程,
class MyMate(type):# 獲得某個類的實(shí)例
def __call__(self, *args, **kwargs):
print('call')
# return super().__call__(*args,**kwargs)
new_args=[]
for i in args:
if isinstance(i,str):
new_args.append(i.upper())
else:
new_args.append(i)
return super().__call__(*new_args, **kwargs)
# 注意注意注意: __new__ __init__ 是創(chuàng)建類對象時還會執(zhí)行
# __call__ 類對象要產(chǎn)生實(shí)例時執(zhí)行
class Student(metaclass=MyMeta):
def __init__(self,name,gender,age):
self.name = name
self.gender = gender
self.age = age
s = Student("jack","woman",18)
print(s.age)
print(s.gender)
class Person(metaclass=MyMeta):
def __init__(self,name,gender):
self.name = name
self.gender = gender
p = Person("rose","man")
print(p.name)
```
總結(jié):當(dāng)你要定制類時,就自定義元類并覆蓋__init__方法
?
異常
異常是程序運(yùn)行過程中發(fā)生的非正常情況,是一個錯誤發(fā)生時的信號
異常如果沒有被正確處理的話,將導(dǎo)致程序被終止,這對于用戶體驗是非常差的,可能導(dǎo)致嚴(yán)重的后果
處理異常的目的就是提高程序的健壯性
異常的分類
python解釋器在執(zhí)行代碼前會先檢查語法,語法檢查通過才會開始執(zhí)行代碼
1.語法檢測異常 作為一個合格的程序員 是不應(yīng)該出現(xiàn)這種低級錯誤
2.運(yùn)行時異常
已經(jīng)通過語法檢測,開始執(zhí)行代碼,執(zhí)行過程中發(fā)生異常 稱之為運(yùn)行時異常
?
TypeError: 'int' object is not subscriptable ? ? 對象不能被切片 ?
TypeError: 'list' object is not callable 對象不能被調(diào)用
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期/課堂內(nèi)容/day29/11.常見異常.py", line 22, in <module>
with open("xxxxx") as f:
FileNotFoundError: [Errno 2] No such file or directory: 'xxxxx'
?
Traceback 是異常追蹤信息 用于展示錯誤發(fā)生的具體位置 以及調(diào)用的過程
其中 包括了 錯誤發(fā)生的模塊 文件路徑 行號 函數(shù)名稱 具體的代碼
最后一行 前面是錯誤的類型
后面 錯誤的詳細(xì)信息 在查找錯誤時 主要參考的就是詳細(xì)信息
必須掌握的語法
語法:
try:
可能會出現(xiàn)異常的代碼 放到try里面
except 具體異常類型 as e:
如果真的發(fā)生異常就執(zhí)行except
如何正確處理異常
當(dāng)發(fā)生異常 不是立馬加try 要先找出錯誤原因并解決它
try 僅在 即使你知道為什么發(fā)生錯誤 ,但是你卻無法避免 例如 你明確告訴用戶 需要一個正確文件路徑 然而用戶依然傳入了錯誤的路徑
如 socket 雙方都要使用管道 ,但是如果一方有由于某些原因強(qiáng)行關(guān)閉了 ,即使你知道原因也無法避免出錯 那就只能try 保證程序正常結(jié)束
總結(jié)一句話:能不加try 就不加try
?
自定義異常類
當(dāng)系統(tǒng)提供異常類不能準(zhǔn)確描述錯誤原因時 就可以自定義異常類
繼承自Exception即可
class ?MyException(Exception):? ?pass
?
主動拋出異常:
什么時候需要主動拋出異常
當(dāng)我們做功能的提供者,給外界提供一個功能接口
但是使用者不按照相應(yīng)的方式來使用,或者參數(shù)類型不正確等原因,導(dǎo)致功能無法正常執(zhí)行時,就應(yīng)該主動拋出異常
主動拋出異常使用raise 關(guān)鍵字
后面可以跟任何Exception的子類 或是 對象
raise MyExceptionraise MyException("錯誤具體原因!")
?
斷言assert
斷言 其實(shí)可以理解為斷定的意思
即非常肯定某個條件是成立的
條件是否成立其實(shí)可以使用if來判斷
其存在的目的就是 為了簡化if 判斷而生的
?
?
?
轉(zhuǎn)載于:https://www.cnblogs.com/komorebi/p/10920853.html
總結(jié)
- 上一篇: Python3 文件读写
- 下一篇: 树——通用树结点数目、高度和度数的实现