1. 程式人生 > 實用技巧 >每日一模組:abc

每日一模組:abc

轉載:https://www.cnblogs.com/Security-Darren/p/4094959.html

最近接觸到了Python中的decorator,metaclass,abc Module,six.add_metaclass等內容,這裡做一個簡單的筆記。

  主要資源:

  1. PEP3119: Abstract Base Classes

  2. abc模組:abc Module,abc—Abstract Base Classes

  3. metaclass: “Python中metaclass解釋”、淺析python的metaclass、PEP3115

  4. 相關:Python 3 初探,第 2 部分: 高階主題

  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。

import six

@six.add_metaclass(Meta)
class MyClass(object):
    pass

在Python 3 等價於

import six

class MyClass(object, metaclass = Meta):
    pass

在Python 2.x (x >= 6)中等價於

import six

class MyClass(object):
    __metaclass__ = Meta
    pass

或者直接用引入裝飾器的目的:

import six

class MyClass(object):
    pass
MyClass  = 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

Method in third-party class, It's right!

Overriding func_a, It's right!

  從執行的結果我們可以看出:

  虛擬子類不算做直接繼承子類,因此可以不實現抽象基類 PluginBase 的任何方法;但直接繼承的子類 SubclassImplementation 必須完全實現抽象基類的抽象方法才能夠例項化(這裡可以註釋掉 26 - 30 行的程式碼實驗)。

  同時,不論是虛擬子類還是直接繼承子類,issubclass()和issubinstance()判斷他們與抽象基類的關係時都返回真值。