日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷(xiāo)解決方案
Python進(jìn)階——元類(lèi)是怎么創(chuàng)建一個(gè)類(lèi)的?

 如果你看過(guò)比較優(yōu)秀的 Python 開(kāi)源框架,肯定見(jiàn)到過(guò)元類(lèi)的身影。例如,在一個(gè)類(lèi)中定義了類(lèi)屬性 __metaclass__,這就說(shuō)明這個(gè)類(lèi)使用了元類(lèi)來(lái)創(chuàng)建。

公司主營(yíng)業(yè)務(wù):成都網(wǎng)站設(shè)計(jì)、成都做網(wǎng)站、移動(dòng)網(wǎng)站開(kāi)發(fā)等業(yè)務(wù)。幫助企業(yè)客戶真正實(shí)現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競(jìng)爭(zhēng)能力。成都創(chuàng)新互聯(lián)是一支青春激揚(yáng)、勤奮敬業(yè)、活力青春激揚(yáng)、勤奮敬業(yè)、活力澎湃、和諧高效的團(tuán)隊(duì)。公司秉承以“開(kāi)放、自由、嚴(yán)謹(jǐn)、自律”為核心的企業(yè)文化,感謝他們對(duì)我們的高要求,感謝他們從不同領(lǐng)域給我們帶來(lái)的挑戰(zhàn),讓我們激情的團(tuán)隊(duì)有機(jī)會(huì)用頭腦與智慧不斷的給客戶帶來(lái)驚喜。成都創(chuàng)新互聯(lián)推出海西免費(fèi)做網(wǎng)站回饋大家。

那元類(lèi)的實(shí)現(xiàn)原理究竟是怎樣的?使用元類(lèi)能幫我們?cè)陂_(kāi)發(fā)中解決什么樣的問(wèn)題?

這篇文章,我們就來(lái)看一下 Python 元類(lèi)的來(lái)龍去脈。

什么是元類(lèi)?

我們都知道,定義一個(gè)類(lèi),然后調(diào)用它的構(gòu)造方法,就可以初始化出一個(gè)實(shí)例出來(lái),就像下面這樣:

 
 
 
  1. class Person(object) 
  2.     def __init__(name): 
  3.         self.name = name 
  4. p = Person('zhangsan')

那你有沒(méi)有想過(guò),我們平時(shí)定義的類(lèi),它是如何創(chuàng)建出來(lái)的?

別著急,我們先來(lái)看一個(gè)例子:

 
 
 
  1. >>> a = 1               # 創(chuàng)建a的類(lèi)是int a是int的實(shí)例 
  2. >>> a.__class__ 
  3.  
  4. >>> b = 'abc'           # 創(chuàng)建b的類(lèi)是str b是str的實(shí)例 
  5. >>> b.__class__ 
  6.  
  7. >>> def c():            # 創(chuàng)建c的類(lèi)是function 方法c是function的實(shí)例 
  8. ...     pass 
  9. >>> c.__class__ 
  10.  
  11. >>> class D(object):    # 創(chuàng)建d的類(lèi)是D d是D的實(shí)例 
  12. ...     pass 
  13. >>> d.__class__ 

在這個(gè)例子中,我們定義了 int、str、function、class,然后分別調(diào)用了它們的__class__ 方法,這個(gè) __class__ 方法可以返回實(shí)例是如何創(chuàng)建出來(lái)的。

從方法返回的結(jié)果我們可以看到:

  •  創(chuàng)建整數(shù) a 的類(lèi)是 int,也就是說(shuō) a 是 int 的一個(gè)實(shí)例
  •  創(chuàng)建字符串 b 的類(lèi)是 str,也就是說(shuō) b 是 str 的一個(gè)實(shí)例
  •  創(chuàng)建函數(shù) c 的類(lèi)是 function,也就是說(shuō) c 是 function 的一個(gè)實(shí)例
  •  創(chuàng)建實(shí)例 d 的類(lèi)是 class,也就是說(shuō) d 是 class 的一個(gè)實(shí)例

除了這些之外,我們?cè)陂_(kāi)發(fā)中使用到的例如 list、dict 也類(lèi)似,你可以測(cè)試觀察一下結(jié)果。

現(xiàn)在我們已經(jīng)得知,創(chuàng)建這些實(shí)例的類(lèi)是 int、str、function、class,那進(jìn)一步思考一下,這些類(lèi)又是怎么創(chuàng)建出來(lái)的呢?

同樣地,我們也調(diào)用這些類(lèi)的 __class__ 方法,觀察結(jié)果:

 
 
 
  1. >>> a = 1
  2. >>> a.__class__.__class__ 
  3.  
  4. >>> 
  5. >>> b = 'abc' 
  6. >>> b.__class__.__class__ 
  7.  
  8. >>> 
  9. >>> def c(): 
  10. ...     pass 
  11. >>> c.__class__.__class__
  12.  
  13. >>> 
  14. >>> class D(object): 
  15. ...     pass 
  16. >>> d = D() 
  17. >>> d.__class__.__class__ 

從結(jié)果我們可以看到,創(chuàng)建這些類(lèi)的類(lèi),都是 type,所以 type 就是創(chuàng)建所有類(lèi)的「元類(lèi)」。也就是說(shuō),元類(lèi)的作用就是用來(lái)創(chuàng)建類(lèi)的。

你可以這樣理解:

  1.  元類(lèi) -> 類(lèi)
  2.  類(lèi) -> 實(shí)例

用偽代碼表示,就是下面這樣:

 
 
 
  1. klass = MetaClass()     # 元類(lèi)創(chuàng)建類(lèi) 
  2. obj = klass()           # 類(lèi)創(chuàng)建實(shí)例

是不是很有意思?

在這里,你也可以感受一下這句話的含義:Python 中一切皆對(duì)象!

無(wú)論是普通類(lèi)型、方法、實(shí)例,還是類(lèi),都可以統(tǒng)一看作對(duì)象,它們的起源就是元類(lèi)。

其實(shí),在 Python 中,使用 type 方法,我們可就以創(chuàng)建出一個(gè)類(lèi),type 方法的語(yǔ)法如下:

 
 
 
  1. type(class_name, (base_class, ...), {attr_key: attr_value, ...})

例如,像下面這樣,我們使用 type 方法創(chuàng)建 MyClass 類(lèi),并且讓它繼承 object:

 
 
 
  1. >>> A = type('MyClass', (object, ), {}) # type創(chuàng)建一個(gè)類(lèi),繼承object 
  2. >>> A 
  3.  
  4. >>> A() 
  5. <__main__.MyClass object at 0x10d905950>

我們還可以使用 type 創(chuàng)建一個(gè)包含屬性和方法的類(lèi):

 
 
 
  1. >>> def foo(self): 
  2. ...     return 'foo' 
  3. ... 
  4. >>> name = 'zhangsan' 
  5. >>> 
  6. # type 創(chuàng)建類(lèi)B 繼承object 包含 name 屬性和 foo 方法 
  7. >>> B = type('MyClass', (object, ), {'name': name, 'foo': foo})  
  8. >>> B.name          # 打印 name 屬性 
  9. 'zhangsan' 
  10. >>> print B().foo() # 調(diào)用 foo 方法 
  11. foo

通過(guò) type 方法創(chuàng)建的類(lèi),和我們自己定義一個(gè)類(lèi),在使用上沒(méi)有任何區(qū)別。

其實(shí),除了使用 type 方法創(chuàng)建一個(gè)類(lèi)之外,我們還可以使用類(lèi)屬性 __metaclass__ 創(chuàng)建一個(gè)類(lèi),這就是下面要講的「自定義元類(lèi)」。

自定義元類(lèi)

我們可以使用類(lèi)屬性 __metaclass__ 把一個(gè)類(lèi)的創(chuàng)建過(guò)程,轉(zhuǎn)交給其它地方,可以像下面這樣寫(xiě):

 
 
 
  1. class A(object): 
  2.     __metaclass__ = ... # 這個(gè)類(lèi)的創(chuàng)建轉(zhuǎn)交給其他地方 
  3.     pass

這個(gè)例子中,我們先定義了類(lèi) A,然后定義了一個(gè)類(lèi)屬性 __metaclass__,這個(gè)屬性表示創(chuàng)建類(lèi) A 的過(guò)程,轉(zhuǎn)交給其它地方處理。

那么,這個(gè)類(lèi)屬性 __metaclass__ 需要怎么寫(xiě)呢?

其實(shí),它可以是一個(gè)方法,也可以是一個(gè)類(lèi)。

用方法創(chuàng)建類(lèi)

如果類(lèi)屬性 __metaclass__ 賦值的是一個(gè)方法,那么創(chuàng)建類(lèi)的過(guò)程,就交給了一個(gè)方法來(lái)執(zhí)行。

 
 
 
  1. def create_class(name, bases, attr): 
  2.     print 'create class by method...' 
  3.     # 什么事都沒(méi)做 直接用type創(chuàng)建了一個(gè)類(lèi) 
  4.     return type(name, bases, attr) 
  5. class A(object): 
  6.     # 創(chuàng)建類(lèi)的過(guò)程交給了一個(gè)方法 
  7.     __metaclass__ = create_class 
  8. # Output:     
  9. # create class by method ...

我們定義了 create_class 方法,然后賦值給 __metaclass__,那么類(lèi) A 被創(chuàng)建時(shí),就會(huì)調(diào)用 create_class 方法。

而 create_class 方法中的邏輯,就是我們上面所講到的,使用 type 方法創(chuàng)建出一個(gè)類(lèi),然后返回。

用類(lèi)創(chuàng)建類(lèi)

明白了用方法創(chuàng)建類(lèi)之后,我們來(lái)看一下用類(lèi)來(lái)創(chuàng)建另一個(gè)類(lèi)。

 
 
 
  1. class B(type): 
  2.     # 必須定義 __new__ 方法 返回一個(gè)類(lèi) 
  3.     def __new__(cls, name, bases, attr): 
  4.         print 'create class by B ...' 
  5.         return type(name, bases, attr) 
  6. class A(object): 
  7.     # 創(chuàng)建類(lèi)的過(guò)程交給了B 
  8.     __metaclass__ = B  
  9.  # Output: 
  10. # create class by B ...

在這個(gè)例子中,我們定義了類(lèi) B,然后把它賦值給了 A 的類(lèi)變量 __metaclass__,這就表示創(chuàng)建 A 的過(guò)程,交給了類(lèi) B。

B 在定義時(shí),首先繼承了 type,然后定義了 __new__ 方法,最后調(diào)用 type 方法返回了一個(gè)類(lèi),這樣當(dāng)創(chuàng)建類(lèi) A 時(shí),會(huì)自動(dòng)調(diào)用類(lèi) B 的 __new__ 方法,然后得到一個(gè)類(lèi)實(shí)例。

創(chuàng)建類(lèi)的過(guò)程

好了,上面我們演示了通過(guò)元類(lèi)創(chuàng)建一個(gè)類(lèi)的兩種方式,分別是通過(guò)方法創(chuàng)建和通過(guò)類(lèi)創(chuàng)建。

其實(shí)創(chuàng)建一個(gè)類(lèi)的完整流程如下:

  1.  檢查類(lèi)中是否有 __metaclass__ 屬性,如果有,則調(diào)用 __metaclass__ 指定的方法或類(lèi)創(chuàng)建
  2.  如果類(lèi)中沒(méi)有 __metaclass__ 屬性,那么會(huì)繼續(xù)在父類(lèi)中尋找
  3.  如果任何父類(lèi)中都沒(méi)有,那么就用 type 創(chuàng)建這個(gè)類(lèi)

也就是說(shuō),如果我們沒(méi)有指定 __metaclass__,那么所有的類(lèi)都是默認(rèn)由 type 創(chuàng)建,這種情況是我們大多數(shù)定義類(lèi)時(shí)的流程。

如果類(lèi)中指定了 __metaclass__,那么這個(gè)類(lèi)的創(chuàng)建就會(huì)交給外部來(lái)做,外部可以定義具體的創(chuàng)建邏輯。

哪種創(chuàng)建類(lèi)的方式更好?

雖然有兩種方式可以創(chuàng)建類(lèi),那么哪種方式更好呢?

一般我們建議使用類(lèi)的方式創(chuàng)建,它的優(yōu)點(diǎn)如下:

  •  使用類(lèi)更能清楚地表達(dá)意圖
  •  使用類(lèi)更加 OOP,因?yàn)轭?lèi)可以繼承其他類(lèi),而且可以更友好地使用面向?qū)ο筇匦?/li>
  •  使用類(lèi)可以更好地組織代碼結(jié)構(gòu)

另外,使用類(lèi)創(chuàng)建一個(gè)類(lèi)時(shí),這里有一個(gè)優(yōu)化點(diǎn):在 __new__ 方法中不建議直接調(diào)用 type 方法,而是建議調(diào)用 super 的 __new__ 來(lái)創(chuàng)建類(lèi),執(zhí)行結(jié)果與 type 方法是一樣的:

 
 
 
  1. class B(type): 
  2.     def __new__(cls, name, bases, attr): 
  3.         # 使用 super.__new__ 創(chuàng)建類(lèi) 
  4.         return super(B, cls).__new__(cls, name, bases, attr)    

創(chuàng)建類(lèi)時(shí)自定義行為

前面我們用元類(lèi)創(chuàng)建一個(gè)類(lèi)時(shí),它的功能非常簡(jiǎn)單。現(xiàn)在我們來(lái)看一下,使用元類(lèi)創(chuàng)建類(lèi)時(shí),如何定義一些自己的邏輯,然后改變類(lèi)的屬性或行為。

我們看下面這個(gè)例子:

 
 
 
  1. # coding: utf8 
  2. class Meta(type): 
  3.     def __new__(cls, name, bases, attr): 
  4.         # 通過(guò) Meta 創(chuàng)建的類(lèi) 屬性會(huì)都變成大寫(xiě) 
  5.         for k, v in attr.items(): 
  6.             if not k.startswith('__'): 
  7.                 attr[k] = v.upper() 
  8.             else: 
  9.                 attr[k] = v 
  10.         return type(name, bases, attr) 
  11. class A(object): 
  12.     # 通過(guò) Meta 創(chuàng)建類(lèi) 
  13.     __metaclass__ = Meta 
  14.     name = 'zhangsan' 
  15. class B(object): 
  16.     # 通過(guò) Meta 創(chuàng)建類(lèi) 
  17.     __metaclass__ = Meta  
  18.     name = 'lisi' 
  19. # 打印類(lèi)屬性 會(huì)自動(dòng)變成大寫(xiě) 
  20. print A.name    # ZHANGSAN 
  21. print B.name    # LISI

在這個(gè)例子中,我們定義了一個(gè)元類(lèi) Meta,然后在定義類(lèi) A 和 B 時(shí),把創(chuàng)建類(lèi)的過(guò)程交給了  Meta,在 Meta 類(lèi)中,我們可以拿到 A 和 B 的屬性,然后把它們的屬性都轉(zhuǎn)換成了大寫(xiě)。

所以當(dāng)我們打印 A 和 B 的屬性時(shí),雖然定義的變量是小寫(xiě)的,但輸出結(jié)果都變成了大寫(xiě),這就是元類(lèi)發(fā)揮的作用。

使用場(chǎng)景

了解了元類(lèi)的實(shí)現(xiàn)原理,那么元類(lèi)都會(huì)用在哪些場(chǎng)景呢?

我們?cè)陂_(kāi)發(fā)中其實(shí)用的并不多,元類(lèi)的使用,經(jīng)常會(huì)出現(xiàn)在一些框架中,例如Django ORM、peewee,下面是使用 Django ORM 定義一個(gè)數(shù)據(jù)表映射類(lèi)的代碼:

 
 
 
  1. class Person(models.Model): 
  2.     # 注意: name 和 age 是類(lèi)屬性 
  3.     name = models.CharField(max_length=30) 
  4.     age = models.IntegerField()     
  5. person = Person(name='zhangsan', age=20) 
  6. print person.name   # zhangsan 
  7. print person.age    # 20

仔細(xì)看在這段代碼中,我們定義了一個(gè) Person 類(lèi),然后在類(lèi)中定義了類(lèi)屬性 name 和 age,它們的類(lèi)型分別是 CharField 和 IntegerField,之后我們初始化 Person 實(shí)例,然后通過(guò)實(shí)例獲取 name 和 age 屬性,輸出的卻是 str 和 int,而不再是 CharField 和 IntegerField。

能做到這樣的秘密就在于,Person 類(lèi)在創(chuàng)建時(shí),它的邏輯交給了另一個(gè)類(lèi),這個(gè)類(lèi)針對(duì)類(lèi)屬性進(jìn)行了轉(zhuǎn)換,最終變成對(duì)象與數(shù)據(jù)表的映射,通過(guò)轉(zhuǎn)換映射,我們就可以通過(guò)實(shí)例屬性的方式,友好地訪問(wèn)表中對(duì)應(yīng)的字段值了。

總結(jié)

總結(jié)一下,這篇文章我們講了元類(lèi)的實(shí)現(xiàn)原理,了解到元類(lèi)是創(chuàng)建所有類(lèi)的根源,我們可以通過(guò) type 方法,或者在類(lèi)中定義 __metaclass__ 的方式,把創(chuàng)建類(lèi)的過(guò)程交給外部。

當(dāng)使用 __metaclass__ 創(chuàng)建類(lèi)時(shí),它可以是一個(gè)方法,也可以是一個(gè)類(lèi)。我們通常會(huì)使用類(lèi)的方式去實(shí)現(xiàn)一個(gè)元類(lèi),這樣做更方便我們組織代碼,實(shí)現(xiàn)面向?qū)ο蟆?/p>

在使用元類(lèi)創(chuàng)建一個(gè)類(lèi)時(shí),我們可以修改創(chuàng)建類(lèi)的細(xì)節(jié),例如對(duì)屬性做統(tǒng)一的轉(zhuǎn)換,或者增加新的方法等等,這對(duì)于我們開(kāi)發(fā)一個(gè)復(fù)雜功能的類(lèi)很友好,它可以把創(chuàng)建類(lèi)的細(xì)節(jié)屏蔽在元類(lèi)中,所以元類(lèi)常常用在優(yōu)秀的開(kāi)源框架中。、


分享文章:Python進(jìn)階——元類(lèi)是怎么創(chuàng)建一個(gè)類(lèi)的?
當(dāng)前路徑:http://m.5511xx.com/article/coccoco.html