python 怎麼更好的理解元類
類即物件
在理解元類之前,需要先掌握Python中的類,Python中類的概念與SmallTalk中類的概念相似。
在大多數語言中,類是用來描述如何建立物件的程式碼段,這在Python中也是成立的:
>>> class ObjectCreator(object):
... pass
...
>>> my_object = ObjectCreator()
>>> print(my_object)
<__main__.ObjectCreator object at 0x8974f2c>
Python中,類其實也是物件。當我們使用關鍵字class
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預設使用它來建立類。當然,我們也可以定義屬於我們自己的元類。