Python進(jìn)階——元類是怎么創(chuàng)建一個類的?
__metaclass__,這就說明這個類使用了元類來創(chuàng)建。什么是元類?
class Person(object)
def __init__(name):
self.name = name
p = Person('zhangsan')
>>> a = 1 # 創(chuàng)建a的類是int a是int的實例
>>> a.__class__
<type 'int'>
>>> b = 'abc' # 創(chuàng)建b的類是str b是str的實例
>>> b.__class__
<type 'str'>
>>> def c(): # 創(chuàng)建c的類是function 方法c是function的實例
... pass
>>> c.__class__
<type 'function'>
>>> class D(object): # 創(chuàng)建d的類是D d是D的實例
... pass
>>> d.__class__
<class '__main__.D'>
int、str、function、class,然后分別調(diào)用了它們的__class__ 方法,這個 __class__ 方法可以返回實例是如何創(chuàng)建出來的。創(chuàng)建整數(shù) a的類是int,也就是說a是int的一個實例創(chuàng)建字符串 b的類是str,也就是說b是str的一個實例創(chuàng)建函數(shù) c的類是function,也就是說c是function的一個實例創(chuàng)建實例 d的類是class,也就是說d是class的一個實例
list、dict 也類似,你可以測試觀察一下結(jié)果。int、str、function、class,那進(jìn)一步思考一下,這些類又是怎么創(chuàng)建出來的呢?__class__ 方法,觀察結(jié)果:>>> a = 1
>>> a.__class__.__class__
<type 'type'>
>>>
>>> b = 'abc'
>>> b.__class__.__class__
<type 'type'>
>>>
>>> def c():
... pass
>>> c.__class__.__class__
<type 'type'>
>>>
>>> class D(object):
... pass
>>> d = D()
>>> d.__class__.__class__
<type 'type'>
type,所以 type 就是創(chuàng)建所有類的「元類」。也就是說,元類的作用就是用來創(chuàng)建類的。元類 -> 類 類 -> 實例
klass = MetaClass() # 元類創(chuàng)建類
obj = klass() # 類創(chuàng)建實例
type 方法,我們可就以創(chuàng)建出一個類,type 方法的語法如下:type(class_name, (base_class, ...), {attr_key: attr_value, ...})
type 方法創(chuàng)建 MyClass 類,并且讓它繼承 object:>>> A = type('MyClass', (object, ), {}) # type創(chuàng)建一個類,繼承object
>>> A
<class '__main__.MyClass'>
>>> A()
<__main__.MyClass object at 0x10d905950>
type 創(chuàng)建一個包含屬性和方法的類:>>> def foo(self):
... return 'foo'
...
>>> name = 'zhangsan'
>>>
# type 創(chuàng)建類B 繼承object 包含 name 屬性和 foo 方法
>>> B = type('MyClass', (object, ), {'name': name, 'foo': foo})
>>> B.name # 打印 name 屬性
'zhangsan'
>>> print B().foo() # 調(diào)用 foo 方法
foo
type 方法創(chuàng)建的類,和我們自己定義一個類,在使用上沒有任何區(qū)別。type 方法創(chuàng)建一個類之外,我們還可以使用類屬性 __metaclass__ 創(chuàng)建一個類,這就是下面要講的「自定義元類」。自定義元類
__metaclass__ 把一個類的創(chuàng)建過程,轉(zhuǎn)交給其它地方,可以像下面這樣寫:class A(object):
__metaclass__ = ... # 這個類的創(chuàng)建轉(zhuǎn)交給其他地方
pass
__metaclass__,這個屬性表示創(chuàng)建類 A 的過程,轉(zhuǎn)交給其它地方處理。__metaclass__ 需要怎么寫呢?用方法創(chuàng)建類
__metaclass__ 賦值的是一個方法,那么創(chuàng)建類的過程,就交給了一個方法來執(zhí)行。def create_class(name, bases, attr):
print 'create class by method...'
# 什么事都沒做 直接用type創(chuàng)建了一個類
return type(name, bases, attr)
class A(object):
# 創(chuàng)建類的過程交給了一個方法
__metaclass__ = create_class
# Output:
# create class by method ...
create_class 方法,然后賦值給 __metaclass__,那么類 A 被創(chuàng)建時,就會調(diào)用 create_class 方法。create_class 方法中的邏輯,就是我們上面所講到的,使用 type 方法創(chuàng)建出一個類,然后返回。用類創(chuàng)建類
class B(type):
# 必須定義 __new__ 方法 返回一個類
def __new__(cls, name, bases, attr):
print 'create class by B ...'
return type(name, bases, attr)
class A(object):
# 創(chuàng)建類的過程交給了B
__metaclass__ = B
# Output:
# create class by B ...
__metaclass__,這就表示創(chuàng)建 A 的過程,交給了類 B。type,然后定義了 __new__ 方法,最后調(diào)用 type 方法返回了一個類,這樣當(dāng)創(chuàng)建類 A 時,會自動調(diào)用類 B 的 __new__ 方法,然后得到一個類實例。創(chuàng)建類的過程
檢查類中是否有 __metaclass__屬性,如果有,則調(diào)用__metaclass__指定的方法或類創(chuàng)建如果類中沒有 __metaclass__屬性,那么會繼續(xù)在父類中尋找如果任何父類中都沒有,那么就用 type創(chuàng)建這個類
__metaclass__,那么所有的類都是默認(rèn)由 type 創(chuàng)建,這種情況是我們大多數(shù)定義類時的流程。__metaclass__,那么這個類的創(chuàng)建就會交給外部來做,外部可以定義具體的創(chuàng)建邏輯。哪種創(chuàng)建類的方式更好?
使用類更能清楚地表達(dá)意圖 使用類更加 OOP,因為類可以繼承其他類,而且可以更友好地使用面向?qū)ο筇匦?/section> 使用類可以更好地組織代碼結(jié)構(gòu)
__new__ 方法中不建議直接調(diào)用 type 方法,而是建議調(diào)用 super 的 __new__ 來創(chuàng)建類,執(zhí)行結(jié)果與 type 方法是一樣的:class B(type):
def __new__(cls, name, bases, attr):
# 使用 super.__new__ 創(chuàng)建類
return super(B, cls).__new__(cls, name, bases, attr)
創(chuàng)建類時自定義行為
# coding: utf8
class Meta(type):
def __new__(cls, name, bases, attr):
# 通過 Meta 創(chuàng)建的類 屬性會都變成大寫
for k, v in attr.items():
if not k.startswith('__'):
attr[k] = v.upper()
else:
attr[k] = v
return type(name, bases, attr)
class A(object):
# 通過 Meta 創(chuàng)建類
__metaclass__ = Meta
name = 'zhangsan'
class B(object):
# 通過 Meta 創(chuàng)建類
__metaclass__ = Meta
name = 'lisi'
# 打印類屬性 會自動變成大寫
print A.name # ZHANGSAN
print B.name # LISI
Meta,然后在定義類 A 和 B 時,把創(chuàng)建類的過程交給了 Meta,在 Meta 類中,我們可以拿到 A 和 B 的屬性,然后把它們的屬性都轉(zhuǎn)換成了大寫。使用場景
Django ORM、peewee,下面是使用 Django ORM 定義一個數(shù)據(jù)表映射類的代碼:class Person(models.Model):
# 注意: name 和 age 是類屬性
name = models.CharField(max_length=30)
age = models.IntegerField()
person = Person(name='zhangsan', age=20)
print person.name # zhangsan
print person.age # 20
Person 類,然后在類中定義了類屬性 name 和 age,它們的類型分別是 CharField 和 IntegerField,之后我們初始化 Person 實例,然后通過實例獲取 name 和 age 屬性,輸出的卻是 str 和 int,而不再是 CharField 和 IntegerField。Person 類在創(chuàng)建時,它的邏輯交給了另一個類,這個類針對類屬性進(jìn)行了轉(zhuǎn)換,最終變成對象與數(shù)據(jù)表的映射,通過轉(zhuǎn)換映射,我們就可以通過實例屬性的方式,友好地訪問表中對應(yīng)的字段值了。總結(jié)
type 方法,或者在類中定義 __metaclass__ 的方式,把創(chuàng)建類的過程交給外部。__metaclass__ 創(chuàng)建類時,它可以是一個方法,也可以是一個類。我們通常會使用類的方式去實現(xiàn)一個元類,這樣做更方便我們組織代碼,實現(xiàn)面向?qū)ο蟆?/span>更多閱讀
特別推薦

點擊下方閱讀原文加入社區(qū)會員
評論
圖片
表情
