這篇文章主要介紹“Python metaclass的原理及應用”,在日常操作中,相信很多人在Python metaclass的原理及應用問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Python metaclass的原理及應用”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
讓客戶滿意是我們工作的目標,不斷超越客戶的期望值來自于我們對這個行業(yè)的熱愛。我們立志把好的技術通過有效、簡單的方式提供給客戶,將通過不懈努力成為客戶在信息化領域值得信任、有價值的長期合作伙伴,公司提供的服務項目有:申請域名、網頁空間、營銷軟件、網站建設、南開網站維護、網站推廣。元編程(meta programming)是一項很神奇的能力,可以通過代碼在運行時動態(tài)生成代碼。
元類(meta classes)是 Python 提供的一種元編程的能力。在 Python 中,類也是一種對象,那么類這種對象就是元類的實例,所以我們可以在運行時通過實例化元類動態(tài)生成類。
首先我們來了解一下 type,type 可以作為函數(shù)使用,用來獲得對象的類型:
>>> class Foo:
... pass
>>> obj = Foo()
>>> obj.__class__
>>>
type(obj)
>>>
obj.__class__ is type(obj)
True
實際上 type 并不是一個函數(shù),而是一個類,我們可以使用 type(type) 來確定一下:
>>> type(type)
type 實際上不只是類,而是一個“元類”。我們接下來要可以看到,所有的元類都需要繼承自 type。type 是所以類的元類,所以在上面的例子中 x 是 Foo 的實例,F(xiàn)oo 是 type 的實例,type 又是他自己的實例。
如果傳遞給 type 的參數(shù)是三個的時候,type 的語義就不再是返回給定參數(shù)的類,而是實例化生成一個新的類。
type(name: str, bases: tuple, namespace: dict)
第一個參數(shù)是新生成的類的名字;第二個參數(shù)是新生成的類的基類列表;第三個參數(shù)是要個這個類綁定的屬性的列表,比如說這個類的一些方法。實際上 class Foo 這種語法只是使用 type 生成類的語法糖而已。
最簡單的一個例子,比如我們要創(chuàng)建 Foo[0..9] 這些類,可以這樣做:
classes = []
for i
in range(10):
cls = type("Foo%s" % i, tuple(), {})
classes.append(cls)
# 就像使用普通類一樣初始化 Foo0
foo0 = clssses[0]()
如果要實現(xiàn)類的方法,一定要記得同樣是要使用 self 變量的。在 Python 中 self 只是一個約定俗成的變量,而不是關鍵字。
def __init__(self, name):
self.name = name
def print_name(self):
print(self.name)
Duck = type("Duck", tuple(), {"__init__": __init__,
"print_name": print_name})
duck = Duck("Donald")
duck.print_name()
# Donald
首先我們來回顧一下 Python 中類的初始化過程:
foo = Foo()
當這條語句運行的時候,Python 會依次調用 Foo 的 __new__
和 __init__
方法。其中 __new__
方法在 __init__
之前調用,并返回已經創(chuàng)建好的新對象,而 __init__
函數(shù)是沒有返回結果的。一般情況下,我們都會覆蓋 __init__
方法來對新創(chuàng)建的對象做一些初始化操作。
現(xiàn)在回歸到元類上,進入燒腦部分。前面我們說過元類的實例化就是類,所以大致相當于:
Foo = MetaFoo(name, bases, attrs) # MetaFoo 默認情況下是 type
foo = Foo()
默認情況下,所有類的元類是 type,也就是在這個類是通過 type 來創(chuàng)建的,這和前面說的通過 type 來動態(tài)創(chuàng)建類也是一致的。
那么怎樣定義一個 MetaFoo 呢?只需要繼承自 type 就行了。因為元類的實例化就是類的創(chuàng)建過程,所以在元類中,我們可以修改 __new__
來在 __init__
之前對新創(chuàng)建的類做一些操作。
>>> class MetaFoo(type):
... def __new__(cls, name, bases, namespace):
... x = super().__new__(cls, name, bases, namespace) # super實際上就是 type
... x.bar =
100 # 為這個類增加一個屬性
... return x
...
>>> Foo = MetaFoo("Foo", tuple(), {}) # MetaFoo 在這里就相當于 type 了,可以動態(tài)創(chuàng)建類
>>> Foo.bar
100
>>> foo = Foo()
>>> foo.bar
100
在這里我們創(chuàng)建了 MetaFoo 這個元類,他會給新創(chuàng)建的類增加一個叫做 bar 的屬性。
在實際的代碼中,我們一般還是不會直接動態(tài)生成類的,還是調用 class Foo
語法來生成類比較常見一點,這時候可以指定 metaclass 參數(shù)就好了??梢酝ㄟ^ Foo(metaclass=MetaFoo) 這種方式來指定元類。
class Foo(metaclass=MetaFoo):
pass
這種定義和上面的元類用法效果完全是一致的。
在 django.models 或者 peewee 等 ORM 中,我們一般使用類的成員變量來定義字段,這里就用到了元類。
class Field:
pass
class IntegerField(Field):
pass
class CharField(Field):
pass
class MetaModel(type):
def __new__(meta, name, bases, attrs):
# 這里最神奇的是:用戶定義的類中的 bases 和 attrs 都會作為參數(shù)傳遞進來
fields = {}
for key, value
in attrs.items():
if isinstance(value, Field):
value.name =
'%s.%s' % (name, key)
fields[key] = value
for base
in bases:
if hasattr(base,
'_fields'):
fields.update(base._fields)
attrs['_fields'] = fields
return type.__new__(meta, name, bases, attrs)
class Model(metaclass=MetaModel):
pass
這樣用戶使用的時候就可以這樣定義:
>>> class A(Model):
... foo = IntegerField()
...
>>> class B(A):
... bar = CharField()
...
>>> B._fields
{'foo': Integer('A.foo'),
'bar': String('B.bar')}
程序在執(zhí)行的時候就可以直接訪問 X._fields
,而不用每次都通過反射遍歷一次,從而提高效率以及做一些驗證。
不過,其實這個完全可以通過裝飾器來實現(xiàn):
def model(cls):
fields = {}
for key, value
in vars(cls).items():
if isinstance(value, Field):
value.name =
'%s.%s' % (cls.__name__, key)
fields[key] = value
for base
in cls.__bases__:
if hasattr(base,
'_fields'):
fields.update(base._fields)
cls._fields = fields
return cls
@model
class A():
foo = IntegerField()
class B(A):
bar = CharField()
但是用裝飾器的話,就失去了一些類型繼承的語義信息。
到此,關于“Python metaclass的原理及應用”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續(xù)學習更多相關知識,請繼續(xù)關注創(chuàng)新互聯(lián)-成都網站建設公司網站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>
當前文章:Pythonmetaclass的原理及應用-創(chuàng)新互聯(lián)
本文網址:http://weahome.cn/article/csgjoc.html