1. 類也是對象python
在大多數編程語言中,類就是一組用來描述如何生成一個對象的代碼段。在Python中這一點仍然成立:編程
>>> class ObjectCreator(object): … pass … >>> my_object = ObjectCreator() >>> print my_object <__main__.ObjectCreator object at 0x8974f2c>
可是,Python中的類還遠不止如此。類一樣也是一種對象。是的,沒錯,就是對象。只要你使用關鍵字class,Python解釋器在執行的時候就會建立一個對象。編程語言
下面的代碼段:ide
>>> class ObjectCreator(object): … pass …
將在內存中建立一個對象,名字就是ObjectCreator。這個對象(類對象ObjectCreator)擁有建立對象(實例對象)的能力。可是,它的本質仍然是一個對象,因而乎你能夠對它作以下的操做:函數
下面是示例:學習
''' 遇到問題沒人解答?小編建立了一個Python學習交流QQ羣:579817333 尋找有志同道合的小夥伴,互幫互助,羣裏還有不錯的視頻學習教程和PDF電子書! ''' >>> print ObjectCreator # 你能夠打印一個類,由於它其實也是一個對象 <class '__main__.ObjectCreator'> >>> def echo(o): … print o … >>> echo(ObjectCreator) # 你能夠將類作爲參數傳給函數 <class '__main__.ObjectCreator'> >>> print hasattr(ObjectCreator, 'new_attribute') Fasle >>> ObjectCreator.new_attribute = 'foo' # 你能夠爲類增長屬性 >>> print hasattr(ObjectCreator, 'new_attribute') True >>> print ObjectCreator.new_attribute foo >>> ObjectCreatorMirror = ObjectCreator # 你能夠將類賦值給一個變量 >>> print ObjectCreatorMirror() <__main__.ObjectCreator object at 0x8997b4c>
2. 動態地建立類測試
由於類也是對象,你能夠在運行時動態的建立它們,就像其餘任何對象同樣。首先,你能夠在函數中建立類,使用class關鍵字便可。ui
>>> def choose_class(name): … if name == 'foo': … class Foo(object): … pass … return Foo # 返回的是類,不是類的實例 … else: … class Bar(object): … pass … return Bar … >>> MyClass = choose_class('foo') >>> print MyClass # 函數返回的是類,不是類的實例 <class '__main__'.Foo> >>> print MyClass() # 你能夠經過這個類建立類實例,也就是對象 <__main__.Foo object at 0x89c6d4c>
但這還不夠動態,由於你仍然須要本身編寫整個類的代碼。因爲類也是對象,因此它們必須是經過什麼東西來生成的纔對。當你使用class關鍵字時,Python解釋器自動建立這個對象。但就和Python中的大多數事情同樣,Python仍然提供給你手動處理的方法。翻譯
還記得內建函數type嗎?這個古老但強大的函數可以讓你知道一個對象的類型是什麼,就像這樣:code
''' 遇到問題沒人解答?小編建立了一個Python學習交流QQ羣:579817333 尋找有志同道合的小夥伴,互幫互助,羣裏還有不錯的視頻學習教程和PDF電子書! ''' >>> print type(1) #數值的類型 <type 'int'> >>> print type("1") #字符串的類型 <type 'str'> >>> print type(ObjectCreator()) #實例對象的類型 <class '__main__.ObjectCreator'> >>> print type(ObjectCreator) #類的類型 <type 'type'>
仔細觀察上面的運行結果,發現使用type對ObjectCreator查看類型是,答案爲type, 是否是有些驚訝。。。看下面
3. 使用type建立類
type還有一種徹底不一樣的功能,動態的建立類。
type能夠接受一個類的描述做爲參數,而後返回一個類。(要知道,根據傳入參數的不一樣,同一個函數擁有兩種徹底不一樣的用法是一件很傻的事情,但這在Python中是爲了保持向後兼容性)
type能夠像這樣工做:
type(類名, 由父類名稱組成的元組(針對繼承的狀況,能夠爲空),包含屬性的字典(名稱和值))
好比下面的代碼:
In [2]: class Test: #定義了一個Test類 ...: pass ...: In [3]: Test() #建立了一個Test類的實例對象 Out[3]: <__main__.Test at 0x10d3f8438>
能夠手動像這樣建立:
Test2 = type("Test2",(),{}) #定了一個Test2類 In [5]: Test2() #建立了一個Test2類的實例對象 Out[5]: <__main__.Test2 at 0x10d406b38>
咱們使用"Test2"做爲類名,而且也能夠把它當作一個變量來做爲類的引用。類和變量是不一樣的,這裏沒有任何理由把事情弄的複雜。即type函數中第1個實參,也能夠叫作其餘的名字,這個名字表示類的名字
''' 遇到問題沒人解答?小編建立了一個Python學習交流QQ羣:579817333 尋找有志同道合的小夥伴,互幫互助,羣裏還有不錯的視頻學習教程和PDF電子書! ''' In [23]: MyDogClass = type('MyDog', (), {}) In [24]: print MyDogClass <class '__main__.MyDog'>
使用help來測試這2個類
In [10]: help(Test) #用help查看Test類 Help on class Test in module __main__: class Test(builtins.object) | Data descriptors defined here: | | __dict__ | dictionary for instance variables (if defined) | | __weakref__ | list of weak references to the object (if defined) In [8]: help(Test2) #用help查看Test2類 Help on class Test2 in module __main__: class Test2(builtins.object) | Data descriptors defined here: | | __dict__ | dictionary for instance variables (if defined) | | __weakref__ | list of weak references to the object (if defined)
4. 使用type建立帶有屬性的類
type 接受一個字典來爲類定義屬性,所以
>>> Foo = type('Foo', (), {'bar':True})
能夠翻譯爲:
''' 遇到問題沒人解答?小編建立了一個Python學習交流QQ羣:579817333 尋找有志同道合的小夥伴,互幫互助,羣裏還有不錯的視頻學習教程和PDF電子書! ''' >>> class Foo(object): … bar = True
而且能夠將Foo當成一個普通的類同樣使用:
>>> print Foo <class '__main__.Foo'> >>> print Foo.bar True >>> f = Foo() >>> print f <__main__.Foo object at 0x8a9b84c> >>> print f.bar True
固然,你能夠向這個類繼承,因此,以下的代碼:
>>> class FooChild(Foo): … pass
就能夠寫成:
>>> FooChild = type('FooChild', (Foo,),{}) >>> print FooChild <class '__main__.FooChild'> >>> print FooChild.bar # bar屬性是由Foo繼承而來 True
注意:
type的第2個參數,元組中是父類的名字,而不是字符串
添加的屬性是類屬性,並非實例屬性
5. 使用type建立帶有方法的類
最終你會但願爲你的類增長方法。只須要定義一個有着恰當簽名的函數並將其做爲屬性賦值就能夠了。
添加實例方法
''' 遇到問題沒人解答?小編建立了一個Python學習交流QQ羣:579817333 尋找有志同道合的小夥伴,互幫互助,羣裏還有不錯的視頻學習教程和PDF電子書! ''' In [46]: def echo_bar(self): #定義了一個普通的函數 ...: print(self.bar) ...: In [47]: FooChild = type('FooChild', (Foo,), {'echo_bar': echo_bar}) #讓FooChild類中的echo_bar屬性,指向了上面定義的函數 In [48]: hasattr(Foo, 'echo_bar') #判斷Foo類中,是否有echo_bar這個屬性 Out[48]: False In [49]: In [49]: hasattr(FooChild, 'echo_bar') #判斷FooChild類中,是否有echo_bar這個屬性 Out[49]: True In [50]: my_foo = FooChild() In [51]: my_foo.echo_bar() True
添加靜態方法
In [36]: @staticmethod ...: def testStatic(): ...: print("static method ....") ...: In [37]: Foochild = type('Foochild', (Foo,), {"echo_bar":echo_bar, "testStatic": ...: testStatic}) In [38]: fooclid = Foochild() In [39]: fooclid.testStatic Out[39]: <function __main__.testStatic> In [40]: fooclid.testStatic() static method .... In [41]: fooclid.echo_bar() True
添加類方法
''' 遇到問題沒人解答?小編建立了一個Python學習交流QQ羣:579817333 尋找有志同道合的小夥伴,互幫互助,羣裏還有不錯的視頻學習教程和PDF電子書! ''' In [42]: @classmethod ...: def testClass(cls): ...: print(cls.bar) ...: In [43]: In [43]: Foochild = type('Foochild', (Foo,), {"echo_bar":echo_bar, "testStatic": ...: testStatic, "testClass":testClass}) In [44]: In [44]: fooclid = Foochild() In [45]: fooclid.testClass() True
你能夠看到,在Python中,類也是對象,你能夠動態的建立類。這就是當你使用關鍵字class時Python在幕後作的事情,而這就是經過元類來實現的。
6. 到底什麼是元類(終於到主題了)
元類就是用來建立類的「東西」。你建立類就是爲了建立類的實例對象,不是嗎?可是咱們已經學習到了Python中的類也是對象。
元類就是用來建立這些類(對象)的,元類就是類的類,你能夠這樣理解爲:
MyClass = MetaClass() #使用元類建立出一個對象,這個對象稱爲「類」 MyObject = MyClass() #使用「類」來建立出實例對象
你已經看到了type可讓你像這樣作:
MyClass = type('MyClass', (), {})
這是由於函數type其實是一個元類。type就是Python在背後用來建立全部類的元類。如今你想知道那爲何type會所有采用小寫形式而不是Type呢?
好吧,我猜這是爲了和str保持一致性,str是用來建立字符串對象的類,而int是用來建立整數對象的類。type就是建立類對象的類。你能夠經過檢查class屬性來看到這一點。Python中全部的東西,注意,我是指全部的東西——都是對象。這包括整數、字符串、函數以及類。它們所有都是對象,並且它們都是從一個類建立而來,這個類就是type。
''' 遇到問題沒人解答?小編建立了一個Python學習交流QQ羣:579817333 尋找有志同道合的小夥伴,互幫互助,羣裏還有不錯的視頻學習教程和PDF電子書! ''' >>> age = 35 >>> age.__class__ <type 'int'> >>> name = 'bob' >>> name.__class__ <type 'str'> >>> def foo(): pass >>>foo.__class__ <type 'function'> >>> class Bar(object): pass >>> b = Bar() >>> b.__class__ <class '__main__.Bar'>
如今,對於任何一個class的class屬性又是什麼呢?
>>> a.__class__.__class__ <type 'type'> >>> age.__class__.__class__ <type 'type'> >>> foo.__class__.__class__ <type 'type'> >>> b.__class__.__class__ <type 'type'>
所以,元類就是建立類這種對象的東西。type就是Python的內建元類,固然了,你也能夠建立本身的元類。
7. metaclass屬性
你能夠在定義一個類的時候爲其添加metaclass屬性。
class Foo(object): __metaclass__ = something… ...省略...
若是你這麼作了,Python就會用元類來建立類Foo。當心點,這裏面有些技巧。你首先寫下class Foo(object),可是類Foo尚未在內存中建立。Python會在類的定義中尋找metaclass屬性,若是找到了,Python就會用它來建立類Foo,若是沒有找到,就會用內建的type來建立這個類。把下面這段話反覆讀幾回。當你寫以下代碼時 :
class Foo(Bar): pass
Python作了以下的操做:
Foo中有metaclass這個屬性嗎?若是是,Python會經過metaclass建立一個名字爲Foo的類(對象)
若是Python沒有找到metaclass,它會繼續在Bar(父類)中尋找metaclass屬性,並嘗試作和前面一樣的操做。
若是Python在任何父類中都找不到metaclass,它就會在模塊層次中去尋找metaclass,並嘗試作一樣的操做。
若是仍是找不到metaclass,Python就會用內置的type來建立這個類對象。
如今的問題就是,你能夠在metaclass中放置些什麼代碼呢?答案就是:能夠建立一個類的東西。那麼什麼能夠用來建立一個類呢?type,或者任何使用到type或者子類化type的東東均可以。
8. 自定義元類
元類的主要目的就是爲了當建立類時可以自動地改變類。一般,你會爲API作這樣的事情,你但願能夠建立符合當前上下文的類。
假想一個很傻的例子,你決定在你的模塊裏全部的類的屬性都應該是大寫形式。有好幾種方法能夠辦到,但其中一種就是經過在模塊級別設定metaclass。採用這種方法,這個模塊中的全部類都會經過這個元類來建立,咱們只須要告訴元類把全部的屬性都改爲大寫形式就萬事大吉了。
幸運的是,metaclass實際上能夠被任意調用,它並不須要是一個正式的類。因此,咱們這裏就先以一個簡單的函數做爲例子開始。
python2中
#-*- coding:utf-8 -*- def upper_attr(future_class_name, future_class_parents, future_class_attr): #遍歷屬性字典,把不是__開頭的屬性名字變爲大寫 newAttr = {} for name,value in future_class_attr.items(): if not name.startswith("__"): newAttr[name.upper()] = value #調用type來建立一個類 return type(future_class_name, future_class_parents, newAttr) class Foo(object): __metaclass__ = upper_attr #設置Foo類的元類爲upper_attr bar = 'bip' print(hasattr(Foo, 'bar')) print(hasattr(Foo, 'BAR')) f = Foo() print(f.BAR)
python3中
''' 遇到問題沒人解答?小編建立了一個Python學習交流QQ羣:579817333 尋找有志同道合的小夥伴,互幫互助,羣裏還有不錯的視頻學習教程和PDF電子書! ''' #-*- coding:utf-8 -*- def upper_attr(future_class_name, future_class_parents, future_class_attr): #遍歷屬性字典,把不是__開頭的屬性名字變爲大寫 newAttr = {} for name,value in future_class_attr.items(): if not name.startswith("__"): newAttr[name.upper()] = value #調用type來建立一個類 return type(future_class_name, future_class_parents, newAttr) class Foo(object, metaclass=upper_attr): bar = 'bip' print(hasattr(Foo, 'bar')) print(hasattr(Foo, 'BAR')) f = Foo() print(f.BAR)
如今讓咱們再作一次,這一次用一個真正的class來當作元類。
#coding=utf-8 class UpperAttrMetaClass(type): # __new__ 是在__init__以前被調用的特殊方法 # __new__是用來建立對象並返回之的方法 # 而__init__只是用來將傳入的參數初始化給對象 # 你不多用到__new__,除非你但願可以控制對象的建立 # 這裏,建立的對象是類,咱們但願可以自定義它,因此咱們這裏改寫__new__ # 若是你但願的話,你也能夠在__init__中作些事情 # 還有一些高級的用法會涉及到改寫__call__特殊方法,可是咱們這裏不用 def __new__(cls, future_class_name, future_class_parents, future_class_attr): #遍歷屬性字典,把不是__開頭的屬性名字變爲大寫 newAttr = {} for name,value in future_class_attr.items(): if not name.startswith("__"): newAttr[name.upper()] = value # 方法1:經過'type'來作類對象的建立 # return type(future_class_name, future_class_parents, newAttr) # 方法2:複用type.__new__方法 # 這就是基本的OOP編程,沒什麼魔法 # return type.__new__(cls, future_class_name, future_class_parents, newAttr) # 方法3:使用super方法 return super(UpperAttrMetaClass, cls).__new__(cls, future_class_name, future_class_parents, newAttr) #python2的用法 class Foo(object): __metaclass__ = UpperAttrMetaClass bar = 'bip' # python3的用法 # class Foo(object, metaclass = UpperAttrMetaClass): # bar = 'bip' print(hasattr(Foo, 'bar')) # 輸出: False print(hasattr(Foo, 'BAR')) # 輸出:True f = Foo() print(f.BAR) # 輸出:'bip'
就是這樣,除此以外,關於元類真的沒有別的可說的了。但就元類自己而言,它們實際上是很簡單的:
如今回到咱們的大主題上來,到底是爲何你會去使用這樣一種容易出錯且晦澀的特性?好吧,通常來講,你根本就用不上它:
「元類就是深度的魔法,99%的用戶應該根本沒必要爲此操心。若是你想搞清楚到底是否須要用到元類,那麼你就不須要它。那些實際用到元類的人都很是清楚地知道他們須要作什麼,並且根本不須要解釋爲何要用元類。」 —— Python界的領袖 Tim Peters