(部落格原創作品,轉載請注明出處!)
最近接觸到了Python中的decorator,metaclass,abc Module,six.add_metaclass等内容,這裡做一個簡單的筆記。
主要資源:
5. six.add_metaclass: six Module
裝飾器的引入純粹是一個“文法糖”,即讓代碼看起來更加易懂。裝飾器引入前Python中已經存在了“class method”, "static method"等包裹函數,不使用裝飾器的結果是如果一個方法要被聲明為class method,那麼在他的“def”語句結束後需要立即使用"classmethod"将其注冊成類方法。這樣有一些弊端:當代碼的讀者開始讀這個函數的時候,他一般看不到末尾的"classmethod"語句,是以可能直到看完整個函數的定義才知道這是一個類方法,也即是最初沒有裝飾器時在定義的結尾對方法進行裝飾的設定比較反人類;另外采用 method = classmethod(method) 方式寫出來的代碼,設計Python的大神們覺得 method 竟然重複出現了兩次太多了,寫這兩次 method 的時間已經夠他們喝一壺的了,是以引入了更為簡潔的decorator。
裝飾器以“@”辨別,實質上是一層包裹函數,即函數的函數。寫在函數定義( def 語句)的前面,表示 def 語句後定義的函數受到裝飾器的裝飾,也就是說這個新定義的函數剛剛出生,立刻在函數定義結束的下一行代碼運作裝飾器給她穿點衣服遮羞。
metaclass是“類的類”,秉承Python“一切皆對象”的理念,Python中的類也是一類對象,metaclass的執行個體就是類(class),自己寫metaclass時需要讓其繼承自type對象。關于metaclass的介紹,“主要資源”中相關的連結,不做贅述。
ABC(抽象基類),主要定義了基本類和最基本的抽象方法,可以為子類定義共有的API,不需要具體實作。
abc子產品,Python 對于ABC的支援子產品,定義了一個特殊的metaclass—— ABCMeta 還有一些裝飾器—— @abstractmethod 和 @abstarctproperty 。
abc.ABCMeta 是一個metaclass,用于在Python程式中建立抽象基類。
抽象基類可以不實作具體的方法(當然也可以實作,隻不過子類如果想調用抽象基類中定義的接口需要使用super())而是将其留給派生類實作。抽象基類可以被子類直接繼承,也可以将其他的類”注冊“(register)到其門下當虛拟子類,虛拟子類的好處是你實作的第三方子類不需要直接繼承自基類但是仍然可以聲稱自己子類中的方法實作了基類規定的接口(issubclass(), issubinstance())!
虛拟子類是通過調用metaclass是 abc.ABCMeta的抽象基類的 register 方法注冊到抽象基類門下的,可以實作抽象基類中的部分API接口,也可以根本不實作,但是issubclass(), issubinstance()進行判斷時仍然傳回真值。
直接繼承抽象基類的子類就沒有這麼靈活,在metaclass是 abc.ABCMeta的抽象基類中可以聲明”抽象方法“和“抽象屬性”,直接繼承自抽象基類的子類雖然判斷issubclass()時為真,但隻有完全覆寫(實作)了抽象基類中的“抽象”内容後,才能被執行個體化,而通過注冊的虛拟子類則不受此影響。
metaclass為 abc.ABCMeta的抽象基類如果想要聲明“抽象方法”,可以使用abc子產品中的裝飾器 @abstractmethod ,如果想聲明“抽象屬性”,可以使用abc子產品中的 @abstractproperty 。
最後,為什麼要提six子產品呢,six子產品是Python為了相容Python 2.x 和Python 3.x提供的一個子產品,該子產品中有一個針對類的裝飾器 @six.add_metaclass(MetaClass) 可以為兩個版本的Python類友善地添加metaclass。這樣我們就可以同時利用Python中的abc子產品和six子產品在類的定義前添加 @six.add_metaclass(abc.ABCMeta) 來優雅地聲明一個抽象基礎類了!
從理論層面打通了,下面上代碼,首先看一下類裝飾器 @six.add_metaclass(MetaClass)的用法,在下面的代碼中,我們希望聲明類 MyClass 的metaclass是類 Meta ,注意類 Meta需要是一個metaclass。
importsix
@six.add_metaclass(Meta)classMyClass(object):pass
在Python 3 等價于
importsixclass MyClass(object, metaclass =Meta):pass
在Python 2.x (x >= 6)中等價于
importsixclassMyClass(object):__metaclass__ =Metapass
或者直接用引入裝飾器的目的:
importsixclassMyClass(object):passMyClass= six.add_metaclass(Meta)(MyClass)
類裝飾器 @six.add_metaclass(MetaClass)的作用是在不同版本的Python之間提供一個優雅的聲明類的metaclass的手段,事實上不用它也可以,隻是使用了它代碼更為整潔明了。
下面結合一個特殊的metaclass即 abc.ABCMeta 來看一段代碼:
import abc
import six
@six.add_metaclass(abc.ABCMeta)
class PluginBase(object):
@abc.abstractmethod
def func_a(self,data):
"""
an abstract method need to be implemented
"""
@abc.abstractmethod
def func_b(self,output, data):
"""
another abstract method need to be implemented
"""
class RegisteredImplementation(object):
def func_c(self, data):
print "Method in third-party class, "+ str(data)
class SubclassImplementation(PluginBase):
def func_a(self,data):
print "Overriding func_a, "+ str(data)
def func_b(self,data):
print "Overriding func_b, "+ str(data)
def func_d(self, data):
print data
PluginBase.register(RegisteredImplementation)
if __name__=='__main__':
for sc in PluginBase.__subclasses__():
print "subclass of PluginBase: " + sc.__name__
print("")
print issubclass(RegisteredImplementation, PluginBase)
print isinstance(RegisteredImplementation(), PluginBase)
print issubclass(SubclassImplementation, PluginBase)
print("")
obj1 = RegisteredImplementation()
obj1.func_c("It's right!")
print("")
obj2 = SubclassImplementation()
obj2.func_a("It's right!")
print ""
上面這端代碼的含義是:
聲明一個metaclass是 abc.ABCMeta 的抽象基類 PluginBase ,為其定義兩個抽象方法,等待派生類的實作。接着定義了一個第三方類 RegisterdImplementation ,将其注冊為類 PluginBase 的虛拟子類。再定義一個子類 SubclassImplementation 直接繼承自抽象基類 PluginBase 。
接着進行試驗,運作結果如下:
subclass of PluginBase: SubclassImplementation
True
True
True
Methodin third-party class, It's right!
Overriding func_a, It's right!
從運作的結果我們可以看出:
虛拟子類不算做直接繼承子類,是以可以不實作抽象基類 PluginBase 的任何方法;但直接繼承的子類 SubclassImplementation 必須完全實作抽象基類的抽象方法才能夠執行個體化(這裡可以注釋掉 26 - 30 行的代碼實驗)。
同時,不論是虛拟子類還是直接繼承子類,issubclass()和issubinstance()判斷他們與抽象基類的關系時都傳回真值。