2012-02-24 76 views
2

我定義幾個類擬用於多重繼承,如:檢測super()是否具有裝飾器的功能?

class A: 
    def __init__(self, bacon = None, **kwargs): 
     self.bacon = bacon 
     if bacon is None: 
      self.bacon = 100 
     super().__init__(**kwargs) 
class Bacon(A): 
    def __init__(self, **kwargs): 
     """Optional: bacon""" 
     super().__init__(**kwargs) 

class Eggs(A): 
    def __init__(self, **kwargs): 
     """Optional: bacon""" 
     super().__init__(**kwargs) 


class Spam(Eggs, Bacon): 
    def __init__(self, **kwargs): 
     """Optional: bacon""" 
     super().__init__(**kwargs) 

不過,我有多個類(如可能BaconASpam,但不​​)是關心他們的時候物業bacon已更改。他們不需要修改價值,只需要知道新價值是什麼,就像事件一樣。由於我已經設置了多重繼承性質,這意味着必須通知超類有關更改(如果它關心的話)。

我知道,如果我將類名傳遞給方法裝飾器,或者如果我使用類裝飾器,這可能是可能的。我不想擁有所有直接的自我類引用,不得不在每個類的上面創建大量裝飾器,或者強制方法使用相同的名稱,因爲這些都不是pythonic。

我希望得到語法看起來是這樣的:

@on_change(bacon) 
    def on_bacon_change(self, bacon): 
     # read from old/new bacon 
     make_eggs(how_much = bacon) 

我不關心的bacon以前的值,使培根的說法是沒有必要的,如果這是bacon之後調用已設置。

  • 是否有可能來檢查,如果超類與此 裝飾的方法?

  • 如果這是不可行的,是否有其他方法可以通過多重繼承鏈傳遞事件如 ?

編輯:

在垃圾郵件功能的實際調用將在一完成,通過使用@property@bacon.setter,因爲這將是初始化bacon最上面級。一旦知道要調用什麼函數self,問題只在於將調用傳遞給MI鏈。

編輯2:

如果我重寫了@bacon.setter屬性,是否可以判斷是否超()類有bacon二傳手?

+1

很明顯OP的意思是「{如果super()有一個函數有裝飾器」,而不是「如果super()有一個{有裝飾器的函數}「 – ninjagecko 2012-02-24 15:42:07

+1

」None「默認主要用於當你真的需要運行一些感興趣的東西來獲得默認值時。這裏你可以使用'def foo(self,bacon = 100,** kwargs ):self.ba con = bacon'雖然你應該使用'def __init __(self,** kwargs):self.bacon = kwargs.get('bacon',100)'如果你試圖支持在任意kwargs周圍洗牌以真正取悅OOP怪人。 – 2012-02-24 15:51:31

+0

@MikeGraham培根的論點是把它從任何通過的'kwargs'中去掉,因爲我不認爲默認的'object'會對它感到滿意。其意圖是,每當到達'object'時,每個參數都被剝離出'kwargs'。 – Darthfett 2012-02-24 17:30:40

回答

2

你叫的是什麼很可能會很好地適應使用更完整的信號框架等等 - 甚至可能邀請面向方面編程。

不過深入研究它,元類和裝飾器可以做你剛纔所要求的 - 我想出了這些,我希望它們適合你。

如果你想把它發展到一些強大和可用的東西,請寫信給我 - 如果沒有這樣的東西存在,那麼保留一個pipy工具包是值得的。

def setattr_wrapper(cls): 
    def watcher_setattr(self, attr, val): 
     super(cls, self).__setattr__(attr, val) 
     watched = cls.__dict__["_watched_attrs"] 
     if attr in watched: 
      for method in watched[attr]: 
       getattr(self, method)(attr, val) 
    return watcher_setattr 

class AttrNotifier(type): 
    def __new__(metacls, name, bases, dct): 
     dct["_watched_attrs"] = {} 
     for key, value in dct.items(): 
      if hasattr(value, "_watched_attrs"): 
       for attr in getattr(value, "_watched_attrs"): 
        if not attr in dct["_watched_attrs"]: 
         dct["_watched_attrs"][attr] = set() 
        dct["_watched_attrs"][attr].add(key) 
     cls = type.__new__(metacls, name, bases, dct) 
     cls.__setattr__ = setattr_wrapper(cls) 
     return cls 


def on_change(*args): 
    def decorator(meth): 
     our_args = args 
     #ensure that this decorator is stackable 
     if hasattr(meth, "_watched_attrs"): 
      our_args = getattr(meth, "_watched_attrs") + our_args 
     setattr(meth, "_watched_attrs", our_args) 
     return meth 
    return decorator 

# from here on, example of use: 
class A(metaclass=AttrNotifier): 
    @on_change("bacon") 
    def bacon_changed(self, attr, val): 
     print ("%s changed in %s to %s" % (attr, self.__class__.__name__, val)) 


class Spam(A): 
    @on_change("bacon", "pepper") 
    def changed(self, attr, val): 
     print ("%s changed in %s to %s" % (attr, self.__class__.__name__, val)) 

a = A() 
a.bacon = 5 

b = Spam() 
b.pepper = 10 
b.bacon = 20 

(在Python 3.2和Python 2.6測試 - 改變 「A」 類的聲明 Python的2元類語法)

編輯 - 上正在做什麼 下面是一些話發生了什麼: 元類選擇標記有「on_close」裝飾器的所有方法,然後在該類的字典中註冊 - 此字典名爲_watched_attrs,它可以作爲普通的類屬性訪問。

元類所做的另一件事是在clas創建後重寫__setattr__方法。這個新的__setattr__只是設置屬性,然後檢查_wacthed_attrs字典,如果該類的任何方法註冊爲在剛更改的屬性被修改時調用 - 如果是,則調用它。

各地watcher_setattr(這是成爲每個類的__setattr__有沒有讓你可以註冊不同的屬性來對繼承鏈中每個類看着這個函數的額外的間接水平 - 所有的類新有自主承擔acessible _watched_attrs字典如果。它不是爲了這個,只有繼承鏈_watched_attrs上最專業的類纔會被尊重。

+0

在我接受之前需要做一些更多的調查,所有這些元和裝飾和遞歸的東西現在超載我。 :P – Darthfett 2012-02-24 19:14:05

+0

在那裏沒有「遞歸」:-)。它看起來很複雜,實際上 - 我會寫一些解釋性文字。 – jsbueno 2012-02-24 19:39:44

+0

無限遞歸是一個單獨的問題,在我看到你的帖子之前,我試圖去思考一個替代方法,哈哈。 非常感謝解釋性文字,我認爲這將回答我的問題(儘管我可以簡化爲只支持單一屬性)。 – Darthfett 2012-02-24 19:58:05

0

您正在尋找蟒蛇性能

http://docs.python.org/library/functions.html#property

override superclass property setter谷歌搜索導致這個StackOverflow的問題:

Overriding inherited properties’ getters and setters in Python

+0

你可能指的是[這個答案](http://stackoverflow.com/a/9343762/936083)?有沒有辦法做到這一點作爲裝飾,或避免直接引用父母的方式? – Darthfett 2012-02-24 17:41:17

+0

我不認爲它適用於'Spam'和'Bacon'有setter的情況,但'Eggs'沒有,因爲'Eggs'不直接從'Bacon'繼承。 – Darthfett 2012-02-24 18:07:08