面向對象之元類 一、什麼是元類 Python中一切皆為對象,對象是有類實例化生成; 類也是對象(類對象),生成類對象的類可稱之為元類; 所以,元類就是來創建類對象的,可稱之為類工廠; type是python內建元類,type是最上層的元類,也可稱為一切類對象的元類 二、元類推導流程 """推導步驟1 ...
目錄
面向對象之元類
一、什麼是元類
- Python中一切皆為對象,對象是有類實例化生成;
- 類也是對象(類對象),生成類對象的類可稱之為元類;
- 所以,元類就是來創建類對象的,可稱之為類工廠;
- type是python內建元類,type是最上層的元類,也可稱為一切類對象的元類
二、元類推導流程
"""推導步驟1:如何查看數據的數據類型"""
# s1 = 'hello world' # str()
# l1 = [11, 22, 33, 44] # list()
# d1 = {'name': 'jason', 'pwd': 123} # dict()
# t1 = (11, 22, 33, 44) # tuple()
# print(type(s1)) # <class 'str'>
# print(type(l1)) # <class 'list'>
# print(type(d1)) # <class 'dict'>
# print(type(t1)) # <class 'tuple'>
"""推導步驟2:其實type方法是用來查看產生對象的類名"""
# class Student:
# pass
# obj = Student()
# print(type(obj)) # <class '__main__.Student'>
"""推導步驟3:python中一切皆對象 我們好奇type查看類名顯示的是什麼"""
class Student:
pass
obj = Student()
print(type(obj)) # <class '__main__.Student'>
print(type(Student)) # <class 'type'>
class A:pass
class B:pass
print(type(A), type(B))
"""
結論:我們定義的類其實都是由type類產生的>>>:元類(產生類的類)
"""
三、創建類的方式
方式一:
創建方法:
直接使用class關鍵字創建
class Foo:
school_name = 'kangkang
def func1(self):
pass
print(Teacher)
print(Teacher.__dict__)
方式二:
創建方法:
使用元類type
type(類名, 類的父類, 類的名稱空間)
cls = type('Student', (object,), {'name':'jason'})
print(cls)
print(cls.__dict__)
"""
1.手動寫鍵值對
針對綁定方法不好定義
2.內置方法exec
能夠運行字元串類型的代碼並產生名稱空間
"""
四、元類定製類的產生行為
通過上述推導,我們得出了我們所創建的類,其實就是type幫我們所生成的,那麼我們是否可以通過繼承type(元類)的方式,來研究type底層代碼原理,來改修,新增條件,來定製類的生成
例如:
生成類時,類名的首字母必須大寫,否則報錯
推導流程:
"""
推導
1、對象是由類名加括弧產生的 __init__
2、類是由元類加括弧產生的 __init__
3、查看type底層源碼,找到__init__
__init__(cls, what, bases=None, dict=None)
cls:元類本身
what:需要產生的類名
bases:產生類的父類
dict:類體名稱空間
4、通過上述我們就可以發現what可以控制類名的產生
"""
# 1、首先生成一個類,繼承元類(type)
class MyMetaClass(type):
pass
# 2、在類中,創建def __init__(self, what, bases=None, dict=None):
# 3、對限制what產生類名的條件
class MyMetaClass(type):
def __init__(self, what, bases=None, dict=None):
# 設置條件,未滿足時,主動拋出異常並提示
if not what.istitle():
raise TypeError('類的首字母要大寫')
# 條件滿足後調用父類進行產生
super().__init__(what, bases, dict)
# 4、指定類的元類:利用關鍵字metaclass指定類的元類
class myclass(metaclass=MyMetaClass):
desc = '元類其實很有趣 就是有點繞'
class Student(metaclass=MyMetaClass):
info = '我是學生 我很聽話'
print(Student)
print(Student.__dict__)
# 5、這個時候我們在生成MyMetaClass的子類時,就必須要遵循它的條件,否則將會報錯
五、元類定製對象的產生行為
要求:
生成的對象必須使用關鍵字進行傳參,否則將無法生成對象
推導流程:
'''
推導:
1、我們在上述學習了類的魔法方法,發現:
2、對象加括弧會執行產生該對象類里的__call__
3、類加括弧會執行產生該類的類里的__call__
4、觀察__call__(self, *args, **kwargs):
self: 調用者本身
*args:接收位置實參
**kwargs:接收關鍵字實參
5、得出結論,我們需要對args進行約束就可達到條件
'''
class MyMetaClass(type):
def __call__(self, *args, **kwargs):
# 1.產生一個空對象(骨架)
# 2.調用__init__給對象添加獨有的數據(血肉)
# 3.返回創建好的對象
# print(args) # ('jason', 18, 'male')
# print(kwargs) # {}
# 設置條件
if args:
# 當條件未滿足時,主動拋出異常,並提升
raise TypeError("需要進行關鍵字傳參")
# 條件滿足後執行,使用super,重新調用父類
return super().__call__(*args, **kwargs)
class Student(metaclass=MyMetaClass):
def __init__(self, name, age, gender):
# print('__init__')
self.name = name
self.age = age
self.gender = gender
# obj = Student('jason', 18, 'male')
obj = Student(name='jason',age= 18,gender= 'male')
print(obj.__dict__)
六、元類之雙下new
當我們在使用類產生對象時,類體代碼種名字產生的順序是:
__ call __ >>>: __ new __ >>>:__ init __
雙下call將類名傳給雙下new,然後new將對象的名字傳給雙下init而後產生類名
得出結論:對象是由new進行產生的
class MyMetaClass(type):
def __call__(self, *args, **kwargs):
# 1.產生一個空對象(骨架)
obj = self.__new__(self)
# 2.調用__init__給對象添加獨有的數據(血肉)
self.__init__(obj,*args, **kwargs)
# 3.返回創建好的對象
return obj
class Student(metaclass=MyMetaClass):
def __init__(self, name):
self.name = name
obj = Student('jason')
print(obj.name)
"""
__new__可以產生空對象
"""