2017-03-31 44 views
0

我正在編寫一個程序來將XML文檔集合轉換爲HTML。這些文檔需要類似但不完全相同的轉換,所以我希望將大部分細節抽象爲一個通用的BaseParser類,然後爲封裝特定於文檔的轉換的每個文檔編寫一個單獨的子類。我正在使用Python標準庫xml.etree.ElementTree包進行基於事件的分析。如何在Python中向類變量註冊方法?

我希望能夠編寫這樣的代碼,其中函數的邏輯與應該調用時的邏輯捆綁在一起。

class CustomParser(BaseParser): 
    @on_tag('word', {'lang':'en'}) 
    def found_en_word(self, tag, attrs): 
     # do something 

對於這項工作,裝飾需要註冊在一個類變量的found_en_word函數(或一個實例變量,雖然這將是冗餘的每個實例具有其自己的副本),從而控制流可以在BaseParser課中分開。

我現在的解決方案,如下所示,是使用元類創建一個callbacks字典的類。

class Meta(type): 
    def __new__(cls, clsname, bases, dct): 
     callbacks = {} 
     for key, value in dct.items(): 
      if hasattr(value, '_on_tag'): 
       callbacks[value._on_tag] = value 
     ret = type(clsname, bases, dct) 
     ret.callbacks = callbacks 
     return ret 

def on_tag(tag, attrs=None): 
    def decorator(f): 
     f._on_tag = (tag, attrs) 
     return f 
    return decorator 

class BaseParser(metaclass=Meta): 
    ... 

不幸的是,看起來並不像元類是繼承了我所希望的方式:它似乎是元類用於構建一個修改BaseParser類,從中CustomParser只是正常繼承。

這個構造可以在Python中使用或不使用元類實現嗎?

回答

1

您的元類沒有正確構造類。如the docs中所述,您實際上需要致電type.__new__(Meta, clsname, bases, dct)。通過調用type(clsname, bases, dct),您正在構造一個不是您的自定義元類實例的普通類。

修復該問題後,您將遇到另一個問題,即您嘗試使用_on_tag作爲字典密鑰,但_on_tag包含字典,且字典不可排除。這與您的主要問題有些相似,但您必須找出一些方法來處理它(可能通過使用戶執行@on_tag('word', ('lang', 'en'))而不是@on_tag('word', {'lang': 'en'}))。

+0

良好的發現,解決了我原來的問題和我不知道的問題!我將讓裝飾器將字典轉換爲元組,以便我可以保留更好的調用語法。 – iafisher