當我們希望動態的產生類的時候,就需要用到元類程式設計的手段。要掌握此技術,需要明白以下幾個基本內容:
metaclass
type
__new__
__call__
在python 中,所有東西都是物件(object),包括python的類(class),也是乙個物件。檢視乙個物件的類,可以用物件的__class__屬性
class
foo(
object):
defhello
(self)
:print
('hello'
)f=foo(
)print
(f.__class__)
#=> __main__.foo
print
(foo.__class__)
#==> type
從上面執行結果可以看到,foo的類是type。所以『類』的初始化是由 「type類」來做。
type是乙個python內建類,所以看不到**,但可看到定義:
class
type
(object):
def__call__
(self,
*args,
**kwargs)
:# real signature unknown
pass
def__init__
(cls, what, bases=
none
,dict
=none):
# known special case of type.__init__
pass
@staticmethod
# known case of __new__
def__new__
(*args,
**kwargs)
:# real signature unknown
pass
#省略其它部分
type 有兩種用法,一是查詢某個物件的類:
#type(object) -> the object's type,e.g.
type
("string"
)# ==>out[2]: str
另乙個則是用來產生乙個類,傳入三個引數: 類名,基類,屬性與方法字典:
# type(class_name, bases, attrs) ,e.g.
deffn
(self)
:print
("hello"
)foo=
type
("foo",(
object,)
,)f=foo(
)f.hello(
)#=> hello
python 直譯器執行時,發現上述class foo的定義後,其實就是翻譯成:
foo=
type
("foo",(
object,)
,)
元類就是用來產生類的類,通過以上講解,應該明白type類就是乙個元類。
我們還可以繼承type類,用自定義的元類可做到:
影響類的定義過程,比如為類自動增加一些方法和屬性
影響定義出來的類的例項化過程,比如為該類的每個例項都做些特殊處理
例子:
class
metabase
(type):
def__new__
(mcs, name, bases=
none
, attrs=
none):
print
('1.建立類時,先呼叫__new__,分配定義類所需的記憶體'
)return
super()
.__new__(mcs, name, bases, attrs)
def__init__
(cls, what, bases=
none
, attrs=
none):
print
('2:然後,呼叫__init__,完成定義'
)return
super()
.__init__(what, bases, attrs)
def__call__
(cls,
*args,
**kwargs)
:print
('例項化定義後的類時,會呼叫__call__'
)return
super()
.__call__(
*args,
**kwargs)
deffn
(self, name=
'world'):
print
('hello {}'
% name)
definit_fn
(self)
:print
('init_fn'
)aclass = metabase(
"aclass",(
object,)
,dict
(hello=fn, __init__=init_fn)
)a = aclass(
)
執行結果:
1.建立類時,先呼叫__new__,分配定義類所需的記憶體
2:然後,呼叫__init__,完成定義
例項化定義後的類時,會呼叫__call__
init_fn
為講清楚上面的例子的執行結果,需要講一下兩個魔法函式:__new__ 與 __call__
python 物件在例項化的第一步,是分配記憶體,分配記憶體就是__new__的責任。為type子類的例項分配記憶體的事,只能由 type.__new__來幹,所以上述例子中最後呼叫的是type.__new__ ,該函式返回的是乙個分配好記憶體的例項,該函式的四個引數分別是:類,名稱,基類,屬性
def
__new__
(mcs, name, bases=
none
, attrs=
none
):
在乙個類上定義了__call__方法後,該類的例項就可當作函式來呼叫
class
callableobject()
:def
__call__
(self)
:print
("hello, i'm like a function!"
)fn = callableobject(
)fn(
)
執行結果
hello, i'm like a function!
在之前的類子中,因為aclass 的類是metabase, 而betabase定義了__call__,所以當建立aclass例項時,列印出了
例項化定義後的類時,會呼叫__call__
在python 3.7 中,可以在申明類的時候,指定 metaclass
class
foo(
object
,metaclass=metabase)
:def
hello
(self)
:print
('hello'
)
在python2 中,是寫成:
class
foo(
object):
__metaclass__= metabase
defhello
(self)
:print
('hello'
)
python 元類程式設計
裝飾器任何時候你定義裝飾器的時候,都應該使用 functools 庫中的 wraps 裝飾器來註解底層包裝函式.因為乙個普通裝飾器作用在某個函式上時,這個函式的重要的元資訊比如名字 文件字串 註解和引數簽名都會丟失。但是 wraps不會。import time from functools impo...
python 元類程式設計
getattr 方法可用來檢查乙個類中是否有乙個屬性,比如 class user def init self,name self.name name def getattr self,item print not find attr def main user user dog user.age i...
Python類元程式設計
類元程式設計是指動態地建立或定製類,也就是在執行時根據不同的條件生成符合要求的類,一般來說,類元程式設計的主要方式有類工廠函式,類裝飾器和元類。通常,我們都是使用 class 關鍵字來宣告乙個類,像這樣 class a name a 但是,我們還有另外一種方式來生成類,下述 與上面作用相同 a ty...