因為類也是對象,所以我們可以在程序運行的時候創(chuàng)建類。Python 是動態(tài)語言。動態(tài)語言和靜態(tài)語言最大的不同,就是函數和類的定義,不是編譯時定義的,而是運行時動態(tài)創(chuàng)建的。在之前,我們先了了解下 type() 函數。
首先我們新建一個 hello.py 的模塊,然后定義一個 Hello 的 class ,
class Hello(object):
def hello(self, name='Py'):
print('Hello,', name)
然后在另一個模塊中引用 hello 模塊,并輸出相應的信息。其中 type() 函數的作用是可以查看一個類型和變量的類型。
#!/usr/bin/env python3
# -*- coding: UTF-8 -*-
from com.twowater.hello import Hello
h = Hello()
h.hello()
print(type(Hello))
print(type(h))
輸出的結果是怎樣的呢?
Hello, Py
<class 'type'>
<class 'com.twowater.hello.Hello'>
上面也提到過,type() 函數可以查看一個類型或變量的類型,Hello 是一個 class ,它的類型就是 type ,而 h 是一個實例,它的類型就是 com.twowater.hello.Hello。前面的 com.twowater 是我的包名,hello 模塊在該包名下。
在這里還要細想一下,上面的例子中,我們使用 type() 函數查看一個類型或者變量的類型。其中查看了一個 Hello class 的類型,打印的結果是: <class 'type'> 。其實 type() 函數不僅可以返回一個對象的類型,也可以創(chuàng)建出新的類型。class 的定義是運行時動態(tài)創(chuàng)建的,而創(chuàng)建 class 的方法就是使用 type() 函數。比如我們可以通過 type() 函數創(chuàng)建出上面例子中的 Hello 類,具體看下面的代碼:
# -*- coding: UTF-8 -*-
def printHello(self, name='Py'):
# 定義一個打印 Hello 的函數
print('Hello,', name)
# 創(chuàng)建一個 Hello 類
Hello = type('Hello', (object,), dict(hello=printHello))
# 實例化 Hello 類
h = Hello()
# 調用 Hello 類的方法
h.hello()
# 查看 Hello class 的類型
print(type(Hello))
# 查看實例 h 的類型
print(type(h))
輸出的結果如下:
Hello, Py
<class 'type'>
<class '__main__.Hello'>
在這里,需先了解下通過 type() 函數創(chuàng)建 class 對象的參數說明:
1、class 的名稱,比如例子中的起名為 Hello
2、繼承的父類集合,注意 Python 支持多重繼承,如果只有一個父類,tuple 要使用單元素寫法;例子中繼承 object 類,因為是單元素的 tuple ,所以寫成 (object,)
3、class 的方法名稱與函數綁定;例子中將函數 printHello 綁定在方法名 hello 中
具體的模式如下:
type(類名, 父類的元組(針對繼承的情況,可以為空),包含屬性的字典(名稱和值))
好了,了解完具體的參數使用之外,我們看看輸出的結果,可以看到,通過 type() 函數創(chuàng)建的類和直接寫 class 是完全一樣的,因為Python 解釋器遇到 class 定義時,僅僅是掃描一下 class 定義的語法,然后調用 type() 函數創(chuàng)建出 class 的 。
不過一般的情況下,我們都是使用 class ***... 的方法來定義類的,不過 type() 函數也可以讓我們創(chuàng)建出類來。也就是說,動態(tài)語言本身支持運行期動態(tài)創(chuàng)建類,這和靜態(tài)語言有非常大的不同,要在靜態(tài)語言運行期創(chuàng)建類,必須構造源代碼字符串再調用編譯器,或者借助一些工具生成字節(jié)碼實現(xiàn),本質上都是動態(tài)編譯,會非常復雜。
可以看到,在 Python 中,類也是對象,你可以動態(tài)的創(chuàng)建類。其實這也就是當你使用關鍵字 class 時 Python 在幕后做的事情,而這就是通過元類來實現(xiàn)的。