Python Mixin技术介绍
1. Mix-in技術介紹
Mixin可以譯為混入,就是在不改變原對象的情況下對其進行擴展。本文介紹了在 Python 語言中,如何實現Mixin技術,及安裝的相應技巧。
1.1. 什么是Mix-in技術
Mix- in技術,中文不知道應該如何稱呼,但意思好象是混入。它的作用是,在運行期間,動態改變類的基類或類的方法,從而使得類的表現可以發生變化。可以用在一 個通用類接口中,根據不同的選擇使用不同的低層類實現,而高層類不用發生變化。而且這一實現可以在運行過程中動態進行改變。由于我也是剛看到,大家有問題 可以與我進行交流。這就是我看到的文章的鏈接。
1.2. 基類的增加
有一個類,
class foo:
??? pass
我可以定義另外一個類,
class foobase:
??? def hello(self):
??????? print "hello"
如果我直接調用:
>>> obj=foo()
>>> obj.hello()
這時你會看到出錯。那么我可以這樣:
>>> foo.__bases__ +=(foobase,)
>>> obj.hello()
hello
成功了。原理是,每個類都有一個bases屬性,它是一個tuple,用來存放所有的基類。而且在運行中,可以動態改變。所以當我們向其中增加新的基類時,再次調用原來不存在的函數,由于基類中的函數已經存在了,所以這次成功了。
這是一個最簡單的應用,可以看到我們可以動態改變類的基類。有幾個注意事項要說一下:
__bases__是一個tuple,所以增加一個值要使用tuple類型,而單個元素tuple的寫法為(foobase,)
類必須先存在。所以,如果想使用這一技術,先要將相關的類的模塊導入(import)。
由于Mix-in是一種動態技術,以多繼承,對象為基礎,而python正好是這樣的語言,使得在python中實現這一技術非常容易。
1.3. 函數替換
在前面,簡單地向大家介紹了一下Mix-in技術,它實現了基類的動態增加。這樣我們就可以在運行時,根據選擇可以動態地增加基類,從而實現不同的目的。現在還有一個問題,就是,在基類與派生類中都有同名的函數,要如何處理呢?
在Python中,如果派生類中有與基類同名的函數,那么調用函數時,會調用派生類的函數,而不是基類的函數,可以測試一下:
>>> class foobase:
??????? def a(self):
??????????????? print "hello"
>>> class foo(foobase):
??????? def a(self):
??????????????? print "foo"
>>> c=foo()
>>> c.a()
foo
可以看出,執行的是foo類的函數。這樣在使用Mix-in技術時,如果原來的類中存在與Mix類中同名的函數,那么Mix類中的函數不會運行,如果想對其進行替換怎么辦呢?方法就是使用getattr()和setattr()函數。當然還是最簡單的。
定義兩個類:
>>> class foobase:
??????? def a(self):
??????????????? print "hello"
>>> class foo:
??????? def a(self):
??????????????? print "foo"
>>> f=getattr(foobase, "a")
>>> setattr(foo, "a", f.im_func)???? #f.im_func會得到真正的函數對象
>>> c=foo()
>>> c.a()
hello
可以看到,函數被替換了。
注意,使用dir(f)還會看到其它的屬性im_class,它表示這個函數屬于哪個類,im_self表示屬于哪個實例。
1.4. Mix-in安裝函數
前面講了基本的實現技術,下面給大家介紹一個Mix-in安裝函數,這個函數是從前面所說的文章copy下來的。
import types
def MixIn(pyClass, mixInClass, makeAncestor=0):
?? if makeAncestor:
???? if mixInClass not in pyClass.__bases__:
??????? pyClass.__bases__ = (mixInClass,) + pyClass.__bases__
?? else:
???? # Recursively traverse the mix-in ancestor
???? # classes in order to support inheritance
???? baseClasses = list(mixInClass.__bases__)
???? baseClasses.reverse()
???? for baseClass in baseClasses:
??????? MixIn(pyClass, baseClass)
???? # Install the mix-in methods into the class
???? for name in dir(mixInClass):
??????? if not name.startswith('__'):
??????? # skip private members
?????????? member = getattr(mixInClass, name)
?????????? if type(member) is types.MethodType:
?????????????? member = member.im_func
?????????? setattr(pyClass, name, member)
這 個函數可以將某個mix-in類安裝為指定類的基類,同時可以通過關鍵字參數指定在基類中的順序,是最前還是最后。因為Python在處理基類時,是安順 序進行的,所以安裝在最前則優先級最高。同時對于指定類的方法如果在mix-in類中存在,則將指定類中的方法替換成mix-in類中的方法。
?? if makeAncestor:
???? if mixInClass not in pyClass.__bases__:
??????? pyClass.__bases__ = (mixInClass,) + pyClass.__bases__
如果makeAncestor為1,表示是安裝在最前,則首先判斷在pyClass的基類中是否存在mixInClass類,如果不存在,再進行安裝。
?? else:
???? # Recursively traverse the mix-in ancestor
???? # classes in order to support inheritance
???? baseClasses = list(mixInClass.__bases__)
???? baseClasses.reverse()
???? for baseClass in baseClasses:
??????? MixIn(pyClass, baseClass)
如 果makeAncestor為0,并不將mixInClass安裝在最后,原作者說他在實際中沒有這樣用的。那么它完成什么任務呢?它實際完成了一個遞 歸,即從mixInClass的最底層的基類開始(因為mixInClass也可能是多重繼承而來的),對pyClass中也存在的函數進行替換。這樣執 行完畢后,mixInClass類中,包含所有基類中的函數,如果有與pyClass類中的函數重名的,都將pyClass中的函數替換成 mixInClass相應的函數。(有些復雜!)
???? # Install the mix-in methods into the class
???? for name in dir(mixInClass):
??????? if not name.startswith('__'):
??????? # skip private members
?????????? member = getattr(mixInClass, name)
?????????? if type(member) is types.MethodType:
?????????????? member = member.im_func
?????????? setattr(pyClass, name, member)
這 步完成重名函數的替換。首先去掉私有方法(私有方法名前有'__').得到mixInClass類中的指定名字的方法對象,判斷是否為方法類型。因為還有 可能取到屬性。在types模塊中包含了一些類型,可以用它來判斷是否為方法類型。對于方法對象,如果是類方法,實際的函數應使用它的屬性 im_func。然后將pyClass相應的方法替換成mixInClass中的方法。
這樣就將mixInClass安裝為pyClass的基類了。
使用例子如:
from classa import classa
from classb import classb
MixIn(classa, classb) #將classb安裝為classa的基類
轉載于:https://www.cnblogs.com/huazi/archive/2012/05/09/2492041.html
總結
以上是生活随笔為你收集整理的Python Mixin技术介绍的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MEF学习笔记(6):出口和元数据
- 下一篇: C语言实现简单线程池