1. 程式人生 > >python 怎麼更好的理解元類

python 怎麼更好的理解元類

類即物件

在理解元類之前,需要先掌握Python中的類,Python中類的概念與SmallTalk中類的概念相似。
在大多數語言中,類是用來描述如何建立物件的程式碼段,這在Python中也是成立的:

>>> class ObjectCreator(object):
...       pass
... 

>>> my_object = ObjectCreator()
>>> print(my_object)
<__main__.ObjectCreator object at 0x8974f2c>

Python中,類其實也是物件。當我們使用關鍵字class

的時候,Python會執行這段程式碼,然後生成一個物件。下面的程式碼在記憶體中建立一個物件ObjectCreator:

>>> class ObjectCreator(object):
...       pass
... 

當一個物件具有建立物件的能力時,就稱該物件為類。

所以類本質上還是一個物件,因此它具有以下屬性:

  • 可以將它賦值給其它變數
  • 可以對它進行復制
  • 可以給它新增屬性
  • 可以將它傳遞給函式作為引數

例如:

>>> print(ObjectCreator) # you can print a class because it's an object
<class
'__main__.ObjectCreator'> >>> def echo(o):
... print(o) ... >>> echo(ObjectCreator) # you can pass a class as a parameter <class '__main__.ObjectCreator'> >>> print(hasattr(ObjectCreator, 'new_attribute')) False >>> ObjectCreator.new_attribute = 'foo
' # you can add attributes to a class >>> print(hasattr(ObjectCreator, 'new_attribute')) True >>> print(ObjectCreator.new_attribute) foo >>> ObjectCreatorMirror = ObjectCreator # you can assign a class to a variable >>> print(ObjectCreatorMirror.new_attribute) foo >>> print(ObjectCreatorMirror()) <__main__.ObjectCreator object at 0x8997b4c>

動態建立類

既然類就是物件,那我們就可以像建立其他物件一樣動態建立類。
首先,在函式中使用class建立一個類:

>>> def choose_class(name):
...     if name == 'foo':
...         class Foo(object):
...             pass
...         return Foo # return the class, not an instance
...     else:
...         class Bar(object):
...             pass
...         return Bar
...     
>>> MyClass = choose_class('foo') 
>>> print(MyClass) # the function returns a class, not an instance
<class '__main__.Foo'>
>>> print(MyClass()) # you can create an object from this class
<__main__.Foo object at 0x89c6d4c>

但是上面的例子也稱不上是完全動態的建立類,因為我們還需要在其中編寫整個類的程式碼。
既然類就是物件,那麼它們肯定是通過某個東西來建立的。當使用class關鍵字的時候,Python會自動建立類,Python也提供了方法讓我們手動來建立類。

還記得type()函式嗎?這個函式可以獲取物件的型別。

>>> print(type(1))
<type 'int'>
>>> print(type("1"))
<type 'str'>
>>> print(type(ObjectCreator))
<type 'type'>
>>> print(type(ObjectCreator()))
<class '__main__.ObjectCreator'>

type還有另外一個功能,那就是建立類。type使用類的相關描述作為引數,然後返回一個類。
type建立類的語法如下:

type(類名,基類元組(可以為空,用於繼承), 包含屬性或函式的字典)

例如:

>>> class MyShinyClass(object):
...       pass

上面的類可以使用下面的方法手動建立:

>>> MyShinyClass = type('MyShinyClass', (), {}) # returns a class object
>>> print(MyShinyClass)
<class '__main__.MyShinyClass'>
>>> print(MyShinyClass()) # create an instance with the class
<__main__.MyShinyClass object at 0x8997cec>

type也接收一個字典引數來定義類中的屬性:

>>> class Foo(object):
...       bar = True

等價於

>>> Foo = type('Foo', (), {'bar':True})

通過type建立的類使用方式跟普通類一樣:

>>> 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 is inherited from Foo
True

最後,我們可能還想給類新增方法,可以先定義一個函式,然後將它以屬性的方式賦予給類。

>>> def echo_bar(self):
...       print(self.bar)
... 
>>> FooChild = type('FooChild', (Foo,), {'echo_bar': echo_bar})
>>> hasattr(Foo, 'echo_bar')
False
>>> hasattr(FooChild, 'echo_bar')
True
>>> my_foo = FooChild()
>>> my_foo.echo_bar()
True

而且,我們還可以在動態建立類之後,給類新增更多的方法和屬性:

>>> def echo_bar_more(self):
...       print('yet another method')
... 
>>> FooChild.echo_bar_more = echo_bar_more
>>> hasattr(FooChild, 'echo_bar_more')
True

什麼是元類?

通常,我們定義類來建立物件,但是現在我們知道類也是物件。那麼是通過什麼來建立類呢?答案就是元類。你可以想象關係如下:

MyClass = MetaClass()
MyObject = MyClass()

你已經知道使用type可以建立類:

MyClass = type('MyClass', (), {})

那是因為type函式實際上就是一個元類,Python使用type作為元類來建立所有的類。
通過檢查class屬性,我們可以知道,其實Python中任何資料型別都是物件,包括整型、字串、函式以及類,它們都是物件。它們都是從類中建立的。

>>> 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__是什麼呢?

>>> age.__class__.__class__
<type 'type'>
>>> name.__class__.__class__
<type 'type'>
>>> foo.__class__.__class__
<type 'type'>
>>> b.__class__.__class__
<type 'type'>

所以類其實就是通過元類來建立的,你可以將元類稱之為類工廠。
type是內建的元類,Python預設使用它來建立類。當然,我們也可以定義屬於我們自己的元類。