python中的元類Metaclass
python中的元類Metaclass
理解元類之前需要學習的知識
如果說讓我們建立一個類,最先想到的肯定是用class
建立,當我們使用class
建立類的時候,Python直譯器自動建立這個物件,但是python同樣也提供了手動處理的方法來建立類,這就是用python的自建函式type()
。
我們所熟知的type()
函式的作用是返回一個引數的型別,但是實際上,它也有一種完全不同的能力,即接受一個類的一些描述作為引數,然後返回一個類。
type()
函式的語法是這樣的:
type(類名, 父類的元組(針對繼承的情況,可以為空),包含屬性的字典(名稱和值))
- 1
- 1
舉個例子:
class ReedSun(ShuaiGe):
shuai = True
def test(x):
return x+2
# 就等價於
type("ReedSun", (ShuaiGe,), {"shuai":True, "test":lambda x: x+2})
# (屬性和方法本質上都是方法)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
在python中,類也是物件,當我們使用class關鍵詞建立一個類的時候,Python直譯器僅僅是掃描一下class定義的語法,然後呼叫type()
函式建立出class。
元類是什麼
元類是什麼?元類實際上就是用來建立類的東西。為了幫助我們理解,我們可以這樣想,我們建立類就是為了建立類的例項,同樣的,我們建立元類就是為了建立類。元類就是類(例項)的類,就像下面這樣
Metaclass() = class
class() = object # object==>例項
- 1
- 2
- 1
- 2
理解了什麼是元類,我們再來看一看type()函式。
其實type就是一個元類,type就是我們用來建立所有的類的元類。(如果我們要建立自己定義的元類的話,也要從type中繼承)
元類的工作原理
我們來看一下下面這個例子
class ReedSunMetaclass(type):
pass
class Foo(object, metaclass = ReedSunMetaclass):
pass
class Bar(Foo):
pass
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
-
首先,我們建立了一個元類
ReedSunMetaclass
(注意!按照預設習慣,元類的類名總是以Metaclass
結尾,以便清楚地表示這是一個元類)。 -
然後,我們又用元類
ReedSunMetaclass
建立了一個Foo
類,(同時,Foo
類的屬性__metaclass__
就變成了ReedSunMetaclass
)。 -
最後,我們建立了一個子類
Bar
繼承自Foo
。
我們來試著理解一下在python內部是怎麼執行這幾個步驟的:
-
對於父類
Foo
,Python會在類的定義中尋找__metaclass__
屬性,如果找到了,Python就會用它來建立類Foo
,如果沒有找到,就會用內建的type來建立這個類。很顯然,它找到了。 -
對於子類
Bar
, python會先在子類中尋找__metaclass__
屬性,如果找到了,Python就會用它來建立類Bar
,如果沒有找到,就再從父類中尋找,直到type。顯然,它在父類中找到了。
我們可以看到使用元類的一個好處了,即他可以讓子類隱式的繼承一些東西。
自定義元類
元類的主要目的就是為了當建立類時能夠自動地改變類。建立類我們需要定義__new__()
函式,__new__
是在__init__
之前被呼叫的特殊方法,是用來建立物件並返回之的方法。我們舉個例子來說明定義自定義元類的方法。
__new__()
方法接收到的引數依次是:
1. 當前準備建立的類的物件;
2. 類的名字;
3. 類繼承的父類集合;
4. 類的方法集合。
class ReedSunMetaclass(type):
def __new__(cls, name, bases, attrs):
# 新增一個屬性
attrs['哈哈哈'] = True
return type.__new__(cls, name, bases, attrs)
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
我們用一個實際例子來說明元類的用法
ORM
就是一個典型的使用元類的例子。ORM
全稱“Object
Relational Mapping”,即物件-關係對映,就是把關係資料庫的一行對映為一個物件,也就是一個類對應一個表,這樣,寫程式碼更簡單,不用直接操作SQL語句。下面我就用這個ORM
的例子來說明一下元類的用法。
#ORM:object relational mapping 物件-關係對映
#把關聯式資料庫的一行對映為一個物件,也就是一個類對應一個表
#ORM框架所有的類只能動態定義
# 定義Field(定義域:元類遇到Field的方法或屬性時即進行修改)
class Field(object):
def __init__(self, name, column_type): # column==>列型別
self.name = name
self.column_type = column_type
# 當用print列印輸出的時候,python會呼叫他的str方法
# 在這裡是輸出<類的名字,例項的name引數(定義例項時輸入)>
# 在ModelMetaclass中會用到
def __str__(self):
return "<%s:%s>" % (self.__class__.__name__, self. name) # __class__獲取物件的類,__name__取得類名
# 進一步定義各種型別的Field
class StringField(Field):
def __init__(self, name):
# super(type[, object-or-type]) 返回type的父類物件
# super().__init()的作用是呼叫父類的init函式
# varchar(100)和bigint都是sql中的一些資料型別
super(StringField, self).__init__(name, "varchar(100)")
class IntegerField(Field):
def __init__(self, name):
super(IntegerField, self).__init__(name, "bigint")
# 編寫ModelMetaclass
class ModelMetaclass(type):
# __new__方法接受的引數依次是:
# 1.當前準備建立的類的物件(cls)
# 2.類的名字(name)
# 3.類繼承的父類集合(bases)
# 4.類的方法集合(attrs)
def __new__(cls, name, bases, attrs):
# 如果說新建立的類的名字是Model,那直接返回不做修改
if name == "Model":
return type.__new__(cls, name, bases, attrs)
print("Found model:%s" % name)
mappings = dict()
for k, v in attrs.items():
if isinstance(v, Field):
print("Found mappings:%s ==> %s" % (k, v)) # 找到對映, 這裡用到上面的__str__
mappings[k] = v
# 結合之前,即把之前在方法集合中的零散的對映刪除,
# 把它們從方法集合中挑出,組成一個大方法__mappings__
# 把__mappings__新增到方法集合attrs中
for k in mappings.keys():
attrs.pop(k)
attrs["__mappings__"] = mappings
attrs["__table__"] = name # 新增表名,假設表名與類名一致
return type.__new__(cls, name, bases, attrs)
# 編寫Model基類繼承自dict中,這樣可以使用一些dict的方法
class Model(dict, metaclass=ModelMetaclass):
def __init__(self, **kw):
# 呼叫父類,即dict的初始化方法
super(Model, self).__init__(**kw)
# 讓獲取key的值不僅僅可以d[k],也可以d.k
def __getattr__(self, key):
try:
return self[key]
except KeyError:
raise AttributeError(r"'Model' object has no attribute '%s'" % key)
# 允許動態設定key的值,不僅僅可以d[k],也可以d.k
def __setattr__(self, key, value):
self[key] = value
def save(self):
fields = []
params = []
args = []
# 在所有對映中迭代
for k, v in self.__mappings__.items():
fields.append(v.name)
params.append("?")
args.append(getattr(self, k, None))
sql = "insert into %s (%s) values (%s)" % (self.__table__, ",".join(fields), ",".join(params))
print("SQL: %s" % sql)
print("ARGS: %s" % str(args))
# 這樣一個簡單的ORM就寫完了
# 下面實際操作一下,先定義個User類來對應資料庫的表User
class User(Model):
# 定義類的屬性到列的對映
id = IntegerField("id")
name = StringField("username")
email = StringField("email")
password = StringField("password")
# 建立一個例項
u = User(id=12345, name="ReedSun", email="sunhongzhao@foxmail.com", password="nicaicai")
u.save()
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
參考資料
相關文章
- 元類:Metaclass
- 2.1.5 Python元類深刻理解(metaclass)Python
- 理解Python中的元類Python
- Python MetaClass深入分析Python
- Python的元類Python
- Python 元類Python
- Python元類與列舉類Python
- Python如何自定義元類Python
- 由ORM談Python元類ORMPython
- python類中的方法Python
- Python基礎之:Python中的類Python
- Python 的元類設計起源自哪裡?Python
- Python中的類與物件Python物件
- python中calss(類)的使用,類的教程,類中的函式怎麼呼叫。Python函式
- Python學習之路41-元類Python
- Python進階丨如何建立你的第一個Python元類?Python
- 徹底搞懂Python中的類Python
- Python 中的元類到底是什麼?這篇恐怕是最清楚的了Python
- 【Spark篇】---SparkStreaming中運算元中OutPutOperator類運算元Spark
- python面試題~反射,元類,單例Python面試題反射單例
- python中類物件及類屬性的介紹Python物件
- python中類和物件的__dict__Python物件
- Python中的類超程式設計Python程式設計
- 如何理解Python3中的子類和父類?Python
- python如何訪問元組中的元素Python
- python類的子類Python
- python 類的子類Python
- Python中類的建立和使用方法Python
- Animal如何修改python3中的類?Python
- Python中的類和物件是什麼Python物件
- 深入理解python中的類和物件Python物件
- python中類的建立和例項化Python
- 元類
- Python 元類機制的工作流程及引數呼叫的不同Python
- 『無為則無心』Python序列 — 20、Python中的元組Python
- Mock 在 Python 單元測試中的使用MockPython
- python的類Python
- Python 簡明教程 --- 20,Python 類中的屬性與方法Python
- 一篇文章搞定Python中的類Python