2013-02-09 45 views
1

我成功地創建裝飾任何類型的類一個裝飾,增加了標準的接口,所有這些,爲方便,集成等...裝飾一堂課 - 裝飾一次忘了吧?

我一直在使用元類的抵制,因爲文學在這一點上說,這是一個矯枉過正的問題,大多數時候都可以被類裝飾器所替代。我遇到的困難是:

def Decorator(somearg): 

    def wrapper(cls): 
     clsinit = cls.__init__ 
     cls.members = [] 

     def __init__(self, *args, **kwargs): 
      #do something with somearg... 
      self.__class__.members.append(self) 
      clsinit(self,*args,**kwargs) 

     cls.__init__ = clsinit 
     return cls 

    return wrapper 

@Decorator('thearg') 
class A(object): 
    pass 

a = A() 
b = A() 

使用python調試器,在導入時,類A立即用參數'thearg'裝飾。但是每次我實例化A()時,實例都直接調用裝飾器中定義的init,而不通過先前的層。這很好,因爲我希望我的課程記錄每個成員,並且不會在每次新實例實例化時都重置。但我不確定我明白爲什麼。

有人可以解釋在這種特定情況下的python解釋器的物理?

+1

您發佈的代碼有問題。它有一個SyntaxError,可能會觸發'somearg'和'cls'的角色。它是你的真實代碼的簡化版本嗎?請測試並修復。 – unutbu 2013-02-09 03:46:12

+0

我修復了代碼 – 2013-02-09 09:25:08

+0

這實際上就是應該使用元類的東西。沒有明確裝飾每個子類,修飾器就無法處理它。 – 2013-02-09 19:17:46

回答

0

OK,指的是更正後的代碼在你更新的問題,你問:

「但每次我實例A()時,實例調用直給init在裝飾定義的 ,而不通過前 層」。

它這樣做是因爲當wrapper()類裝飾功能,由Decorator()函數創建,適用於class A,它取代A__init__()有自己的動態定義wrapper.__init__()功能。 功能就是當實例化A時被調用的函數,它首先被寫入做一些事情,然後在最後調用原始的A.__init__()

+0

我修復了代碼 – 2013-02-09 09:25:30

2

如果你只是改變了類,而不是創建一個新的,沒有必要爲一個包裝函數(本來應該是包裝類):

def Decorator(cls): 
    clsinit = cls.__init__ 
    cls.members = [] 

    def __init__(self, *args, **kwargs): 
     clsinit(self, *args, **kwargs) 
     self.__class__.members.append(self) 

    cls.__init__ = __init__ 
    return cls 

BTW,你應該建立一個基類繼承它:

class Base(object): 
    members = [] 
    def __init__(self): 
     self.__class__.members.append(self) 

class A(Base): 
    pass 

更清潔

+0

我在代碼上犯了一個錯誤:我現在編輯它。我做了包裝函數,因爲我需要在裝飾時使用參數來以某種方式來實例化咖啡。另外,我試圖繼承!但我的問題是,母類成爲所有子類的容器,這是不受歡迎的行爲。如果我裝飾,所有裝飾的課程成爲他們自己的容器。 – 2013-02-09 08:55:17

0

感謝@JBernardo和@martineau的回答。我用這段代碼的目標是實現類對象的容器類行爲,以便更容易地訪問它們的實例。但是我想一次訪問相對於每個類的實例,而不是全部都在同一個繼承類中。

實際工作的代碼再次幫助我理解我的問題:

一)裝飾被調用,其參數somearg

B)的下一行(A類的定義)的內容作爲包裝函數的參數。1)記住初始類init以避免無限遞歸 2)定義一個新的init來在實例級別進行更改 3)綁定類的新init方法 4)返回轉換後的類對象

c)因爲return是一個類對象,每次我實例化一個實例時,我會直接進入init,因爲這是它在新類中的綁定方式。