python元类简述
面向?qū)ο缶幊?/strong>
python 是一門按照面向?qū)ο笏季S設計的一門編程語言,而理解面向?qū)ο蟮恼Z言關(guān)鍵在于一句話,“一切皆對象”,函數(shù)是一個對象,一個實例是對象,一個類也是對象。如果對象被創(chuàng)建了,那么在這個程序管理的內(nèi)存區(qū)域中,就會真實的存在一片區(qū)域去保存這個對象的信息。因此,我們在代碼中定義一個函數(shù),定義一個類,即使我們并沒有調(diào)用這個函數(shù)或者去實例化這個類,在內(nèi)存空間中,也已經(jīng)存在了這個函數(shù)對象和類對象。例如下面的代碼。
class A:def __init__(self, a, b):self.a = aself.b = b代碼執(zhí)行后,內(nèi)存中會創(chuàng)建一個類對象,且類對象上顯式的綁定了__init__方法。
類
我們使用class A: pass這樣的語句可以定義一個類,但是我們并沒有在意它是如何被創(chuàng)建的,實際上這個語句的完整寫法為
class A(metaclass=type):pass這里metaclass=type 的意思是這個A類使用type類來創(chuàng)造,也就是使用type類作為他的元類,如果我們不聲明,就默認使用type來創(chuàng)建這個類,而type也是一個類對象,這個類對象的功能,就是創(chuàng)建一個類,所以他與我們自己定義的普通的類又有所區(qū)別,所以有了metaclass,也就是元類的稱呼。
何為元類
元類:簡單的一句話就是創(chuàng)建類的類。首先元類也是一個類,,所以元類也是由另一個元類創(chuàng)建的,但是創(chuàng)建一個元類必須指定這個元類將繼承哪個元類。下面看一些元類和普通類的區(qū)別。
''' 遇到問題沒人解答?小編創(chuàng)建了一個Python學習交流QQ群:778463939 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' class MetaclassA(type): # 繼承type類,并由type類創(chuàng)建,定義了一個元類passclass MetaclassB(MetaclassA): # 繼承MetaclassA,由type創(chuàng)建,定義了一個元類passclass MetaclassB(MetaclassA, metaclass=MetaclassB): # 繼承MetaclassA,由MetaclassBpass class C: # 由type 類創(chuàng)建,繼承object。是一個普通類passclass D(metaclass=MetaclassA): # 由metaclassA 創(chuàng)建,繼承object,是一個普通類pass總結(jié)為兩點
- 類在創(chuàng)建時可以指定自己被哪個元類創(chuàng)建,使用metaclass參數(shù)指定一個元類即可,沒指定默認為type元類
- 定義的類是普通類還是元類,看他是直接繼承自元類及其子類,還是繼承自object及其子類。
元類的作用
元類的各類框架中使用較為廣泛,由于自定義元類的參與,使得我們自己定義的類在創(chuàng)建時候,可以方便的進行一些自定義的初始化,完成我們想要的功能。在日常的編碼中的一般很少使用元類,但是在閱讀源碼的過程中卻常遇到,作者利用元類做一些非常精巧的設計去實現(xiàn)使用其他方法很難完成得功能。從下面的使用中體會元類的使用
元類的使用
默認情況下,類的創(chuàng)建都是交給 type元類 去創(chuàng)建,創(chuàng)建后的類有自己的默認的一些屬性和方法,也有一部分從object類繼承的屬性和方法。如果我們想要在需要定義一個元類來實現(xiàn)類的自定義初始化,就必須繼承自type或者及其子類。type類中默認創(chuàng)建類對象的方法為__new__(cls) 方法。我們可以重寫這個方法。
class MetaClassA(type):def __new__(mcs, name, base, attrs):'''mcs,這個元類對象自己name:將要被創(chuàng)建的類的名字,下面的代碼中 定義了一個 A 類,則這個name 為 "A" 這個字符串base: 被創(chuàng)建的類如果指定了需要繼承某個類,base中將會紀錄他所有的父類對象。attrs:被創(chuàng)建的類中定義的類屬性和方法的字典,字典的key 為屬性的變量名,value為對應的屬性值'''# 查看這四個參數(shù)的值print(mcs, name, base, attrs) # 類名,父類元組,類屬性字典。'''輸出的值msc: <class '__main__.Metaclass'>name: Abase: (<class 'object'>,)attrs: {'__module__': '__main__', '__qualname__': 'A', 'xxx': 1, 'x': <function A.<lambda> at 0x00000232DFF34620>, 'Meta': <class '__main__.A.Meta'>, '__init__': <function A.__init__ at 0x00000232DFF346A8>}'''# 查看參數(shù)值后,需要調(diào)用type類的new方法創(chuàng)建因為python 只提供了type元類來調(diào)用系統(tǒng)調(diào)用來申請內(nèi)存空間,創(chuàng)建一個類對象并返回cls = type.__new__(mcs, name, base, attrs)# 返回值則是 A 這個類對象,我們可以使用dir查看類中的屬性和方法,可以看到 上面attrs中的值,并新增了很多k-v對print(cls, dir(cls))return cls # 必須使用 return 將這個類對象的返回,下面定義的class A 的 A 變量才能收到這個cls 對象,否則 A 的值為None, 即使使用了class A這個語法。# 我們使用MetaClassA 創(chuàng)建 類A class A(object, metaclass=MetaclassA):# 為 A定義兩個類屬性xxx = 1yyy = lambda x: x + 1# 定義一個類, 這個類的也是A類的一個屬性,可以通過A.Meta訪問到這個類對象class Meta:x = []y = 1# 定義一個方法def __init__(self):self.a = 1 self.b = 2通過上面的代碼可以了解 一個類創(chuàng)建 的過程,在metaclassA的 ?__new__()?方法中,我們獲取到了在 A 類中定義的所有類屬性和方法,這些屬性和方法都被收集到attrs 這個字典中,并交給type.__new__()方法用于創(chuàng)建類,所以想要操作類中的屬性和方法,可以對attr字典進行想要的操作。例如我們?yōu)樗械念惗继砑右粋€方法。
''' 遇到問題沒人解答?小編創(chuàng)建了一個Python學習交流QQ群:778463939 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' def func():print("我是被 MetaClassA 創(chuàng)建的")class MetaClassA(type):def __new__(mcs, name, base, attrs):print(mcs, name, base, attrs) # 類名,父類元組,類屬性字典。attrs["show"] = func # 將func 這個函數(shù)添加到A 的方法中,這樣 A 類會有一個show方法,調(diào)用后會執(zhí)行func函數(shù)。return type.__new__(mcs, name, base, attrs)動態(tài)的創(chuàng)建類
動態(tài)的創(chuàng)建類 是指 直接通過 元類的方法來創(chuàng)建類,而不在使用 class 這個關(guān)鍵字來創(chuàng)建類對象。
使用關(guān)鍵字定義
動態(tài)的創(chuàng)建
A = type.__new__(type, "A", (), {"a":1, "b":2, "show": lambda :pass})這樣的代碼和上述的結(jié)果是相同的,或者還可以使用更直接的方法。我們知道普通類創(chuàng)建實例時候,可以ins = ClassName(*args)的方式創(chuàng)建一個實例對象 ins,同樣,元類也可以如此創(chuàng)造一個類
A = type("A", (), {"a":1, "b":2, "show": lambda :pass})對一個元類使用類似實例化的方法,得到了一個類對象。
綜合一下:
''' 遇到問題沒人解答?小編創(chuàng)建了一個Python學習交流QQ群:778463939 尋找有志同道合的小伙伴,互幫互助,群里還有不錯的視頻學習教程和PDF電子書! ''' class Metaclass(type):def __new__(cls, class_name: str, bases: tuple, attrs: dict):print(1, cls)print(2, class_name)print(3, bases)return type.__new__(cls, class_name, bases, attrs)# 1. 創(chuàng)建一個類名為 A 的類, class A(metaclass=Metaclass):a = 100# 2. 創(chuàng)建一個類名為 B 的類,使用 BClass變量接收 BClass = Metaclass.__new__(Meta, 'B', (), {"a":100}) # 創(chuàng)建一個B類 print(BClass)# 3. 創(chuàng)建一個類名為 C 的類,,使用 CClass變量接收 CClass = Metaclass("C", (BClass, ), {"a": 100}) # 創(chuàng)建一個C類,并繼承B類 print(CClass)上面三個類的創(chuàng)建過程完全一致,處理類名不同和內(nèi)存中的儲存的地址不同,所擁有的屬性和方法都是一樣的。
元類的使用實例(django-orm中元類的簡單使用原理)
Django的ORM中的Model類使用了元類來實現(xiàn)“表-類”,“實例-一條記錄”,“實例屬性-字段”的對應關(guān)系
class MetaModel(type):def __new__(mcs, class_name: str, bases: tuple, attrs: dict):# 收集所有的字段,并添加一些默認需要的屬性,并提取設置為主鍵的 字段attrs.setdefault("db_table", class_name.lower()) # 設置表名for key, field in attrs:if isinstance(field, Field):if field.pk: # 該字段被設置了主鍵,定義設置主鍵名字__primary__ = field.name or key if not field.name: # 沒有自定義名字,使用屬性名作為在field.name = keyreturn type.__new__(mcs, class_name, bases, attrs)# 字段對象,記錄可字段的名字,類型,是否為null, 是否為主鍵等等元信息,和數(shù)據(jù)庫中字段的元數(shù)據(jù)對應 class Field:def __init__(self, name=None, typ="varchar", null=None, primary_key=False):self.name = nameself.type = typself.null = nullself.pk = primary_keydef __repr__(self):return "<{}-{}-{}-{}>".format(self.name, self.type, self.null, self.pk)# 表對象,每一個類屬性就是一個字段對象 class Person(metaclass=MetaModel):# 創(chuàng)建這個類時候,將會調(diào)用 MetaModel的new方法,這樣定義的每個字段都會被 metaclass中attrs 收集并操作id = Field("id", primary_key=True)name = Field("name", )age = Field("age", null=False)通過使用元類,在我們將這個Person類屬性字典進行了操作,自動建立了一些屬性信息,例如db_table將使用該名字作為數(shù)據(jù)庫中表名,指定了屬性__primary__作為主鍵。類似于Django中的做法,這里只是簡單的演示元類的應用。
總結(jié)
以上是生活随笔為你收集整理的python元类简述的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python教程:列表(list)、元组
- 下一篇: 解析Python中的线程与进程