日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 >

29 元类 异常

發(fā)布時間:2025/3/20 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 29 元类 异常 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

什么是元類

  一切皆對象?

  類也是對象,可以把一個類當(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

__init__方法

實(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ì)信息

異常處理

異常發(fā)生后 如果不正確處理將導(dǎo)致程序終止,我們必須應(yīng)該盡量的避免這種情況發(fā)生

必須掌握的語法

語法:

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 MyException
    raise MyException("錯誤具體原因!")

    ?

    斷言assert

    斷言 其實(shí)可以理解為斷定的意思

    即非常肯定某個條件是成立的

    條件是否成立其實(shí)可以使用if來判斷

    其存在的目的就是 為了簡化if 判斷而生的

    ?

    ?
























    ?

    轉(zhuǎn)載于:https://www.cnblogs.com/komorebi/p/10920853.html

    總結(jié)

    以上是生活随笔為你收集整理的29 元类 异常的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。