1. 程式人生 > >深入淺出Python超程式設計

深入淺出Python超程式設計

隔壁的Java 世界為了建立一個物件搞得雞飛狗跳,這邊的Python直譯器倒是樂得清閒。 

(參見:《當建立物件時......》)

我作為他的第n任助手正式上崗。

“老大,有程式設計師要建立物件,怎麼辦?”我向Python直譯器發出了預警,上崗後頭一次遇到這種情況,我有點緊張。

class Person:
    def sayHello(self,name):
        print("hello,"+name)

p = Person()

p.sayHello("andy")

“怕啥,我告訴你怎麼做啊,首先找到Metaclass(元類),用元類來建立Class, 最後用Class物件來建立例項。”   老大說著還給我畫了個圖: 

 “不是吧!剛才還說人家Java雞飛狗跳,我看我們這兒也絲毫不差,一個Class(如Person)在記憶體中用個物件來表示我理解,畢竟在我們的世界中,一切都是物件嘛, 但是這Metaclass(元類)是什麼鬼?”  

“是啊,類是一個物件,呼叫這個類物件的__new__方法就可以創建出這個類的例項。那麼問題來了, 類物件是怎麼來的?怎麼把這個類物件給new 出來?”  老大沒有回答,只是反問。  

“不是程式設計師寫的嗎, class Person.....”  我有點底氣不足。 

“程式設計師寫的只是程式碼,都是文字而已,我們在執行的過程中需要用Metaclass 把這個Person類物件給建立起來的。” 

“可是我也沒有看到Person類的Metaclass啊?! 他到底在哪兒?”  

“那是你沒有找到, Person類中沒有,就去它的父類中去找,如果也沒有,就繼續向父類的父類去找,如果在任何父類中都找不到Metaclass,就去模組中找,如果還是找不到,就用預設的Metaclass,即type。” 

我按照老大的要求,去找這個Metaclass,沒有找到,只好用預設的type了。

可是我記得這個type不是個類,是個函式啊,可以用來檢視一個變數的型別:

>>> type(1)
<class 'int'>
>>> type("aaa")
<class 'str'>
>>>

老大說:“這個type啊,還有另外一個用法,可以建立其他類物件,在建立的時候,需要三個引數:”

1. 要建立的類物件的名稱,例如"Person" 

2. 要建立的類物件的父類,例如(object) 

3. 包含屬性的字典,即類的屬性和方法。例如{"sayHello": sayHello} 

比如,下面這段程式碼也建立了一個類物件Person,和程式設計師寫的class Person...  效果是一樣的。

def sayHello(self,name):
    print("hello,"+name)

#通過type來建立一個類物件,名稱為Person,這個類物件有一個方法sayHello
Person = type("Person",(),{"sayHello":sayHello})

#通過類物件來建立例項
p = Person()

p.sayHello("andy")  # hello andy 

(友情提示:可左右滑動)

嘿,這個辦法不錯啊,可以在執行時、動態地建立一個全新的類出來!隔壁的Java雖然也能做到,但是得利用ASM之類工具去直接操作位元組碼,太麻煩了,我大Python直接通過普普通通、簡簡單單的Python程式碼就搞定了!

這就是動態指令碼語言的一個優勢吧! 

之前聽說過超程式設計,現在應該就是超程式設計了吧?但是這個Metaclass到底有什麼用處呢? 程式設計師為什麼不直接在程式碼中寫class Person..... 這樣的程式碼? 這樣多直觀啊。

老大說:“有些程式設計師會自定義Metaclass,這些自定義的Metaclass 主要做這些事情:” 

1. 攔截類的建立 

2. 讀取類的資訊,可能做修改 

3. 返回新的類。 

攔截類的建立? 為什麼有這樣“變態”的需求?

我真想看看一個自定義的Metaclass,看看它到底是怎麼“變態”的。 

沒多久,機會來了,又要建立物件了。 

from django.db import models

class Employee(models.Model):
    name = models.CharField(maxlength = 50)    
    age  = models.IntegerField()
    #其他程式碼略#

在Employee中沒有看到Metaclass, 我就去父類Model中去尋找,運氣不錯,一下子就找到了metaclass ,叫做ModelBase:

class Model(metaclass=ModelBase):
    #其他程式碼略

趕緊去看ModelBase的程式碼,唉,實在是有點複雜了,讓我看得頭暈。 

老大說:“你不用花費時間了,你的前任的前任曾經研究過它,是為了實現ORM !” 

“ORM?” 

“就是物件和關係資料庫的對映。你想想,程式設計師建立的Python物件想要儲存到資料庫中,該怎麼辦?“ 老大問道。

”那還不簡單,程式設計師可以寫SQL程式碼啊,insert into employee(name,age) values(?,?),其中包含那個Employee物件的name ,age的值不就行了?“

”那樣就有點笨拙了,你再想想,能不能簡化程式設計師的工作,別讓他們去寫這些煩人的、容易出錯的SQL程式碼?能不能讓框架來做這件事?“ 老大寫了兩行程式碼。

employee = Employee(name="andy",age=20)  
employee.save()

“看看,程式設計師只要把物件創建出來,呼叫下save方法就行了,SQL語句就會形成,儲存到資料庫中。”

(注:這裡略過了資料庫連線的管理)

“難道ModelBase這個元類在後面做‘手腳'?”我似乎有點理解了。

”沒錯,你看到這些Employee類的屬性沒有? 就是程式設計師寫的那些name, age...... 程式設計師這麼寫,其實就是在告訴ModelBase,尊敬的Metaclass 啊, 這些都是資料庫的列啊,列名是 name, 型別是char(50) , 還有個列名是age,是個整數。” 

 “那個MetaClass ,對,就是ModelBase會讀取這些列名、型別,並且記錄下來。 有了列名的資訊,將來就可以形成insert, update,delete等SQL語句了。對不對?”

原來如此!看來ModelBase在建立Employee類物件的時候,“偷偷地”讀取了Employee類的定義資訊,這樣才能在背後實現ORM! 

我按照老大的指示,呼叫ModelBase的__new__方法,建立了Employee類物件。 

接下來又呼叫Employee類物件的__new__方法,建立了Employee例項物件。  

employee = Employee(name="andy",age=20)  
employee.save()

當程式設計師呼叫employee.save()方法的時候,正如老大所說,神奇的魔法發生了,一條sql語句形成,並且傳送給了資料庫去執行。

我感慨到:“這Python的超程式設計還是真是不錯啊,能在執行時動態地修改類,比隔壁的Java強多了!”

“Python超程式設計的技術不僅僅是Metaclass,還多著呢,你慢慢學吧!”