深入浅出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?modelsclass?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,還多著呢,你慢慢學吧!”
總結
以上是生活随笔為你收集整理的深入浅出Python元编程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 操作系统和Web服务器那点事儿
- 下一篇: 学习编程的加速度