1

我有一個繼承父級裝飾屬性的類。我想添加一個裝飾器(@thedecorator1("yyy")),但不覆蓋整個方法和現有裝飾器。 (裝飾的順序很重要,在一定程度上,thedecorator1應該property.setter和thedecorator2之間總是)如何在Python中將裝飾器添加到繼承的方法而不復制整個方法?

這有可能在Python 3?

from functools import wraps 

class thedecorator1: 
    def __init__ (self, idx): 
     self.idx = idx 
    def __call__ (self, func): 
     @wraps(func) 
     def wrapped(*args, **kwargs): 
      print("thedecorator1", self.idx, args) 
      return func(*args, **kwargs) 
     return wrapped 

def thedecorator2(func): 
    def _thedecorator2(self, *args, **kwargs): 
     print("thedecorator2",args) 
     return func(self, *args, **kwargs) 
    return _thedecorator2 

class SomeClass: 
    """That's te base class""" 
    _some_property = None 

    @property 
    @thedecorator2 
    def someproperty(self): 
     return self._some_property 

    @someproperty.setter 
    @thedecorator1("xxx") 
    @thedecorator2 
    def someproperty(self, value): 
     self._some_property = value 

class AnotherClass(SomeClass): 
    """That's what works, but I need to copy everything everytime""" 

    _some_property = None 

    @property 
    @thedecorator2 
    def someproperty(self): 
     return self._some_property 

    @someproperty.setter 
    @thedecorator1("yyy") 
    @thedecorator1("xxx") 
    @thedecorator2 
    def someproperty(self, value): 
     self._someproperty = value 

class YetAnotherClass(SomeClass): 
    """That's what I think I need""" 

    dosomethingsmart(target = someproperty, decorator = thedecorator1("yyy"), after=someproperty.setter) 
+0

請注意,這是有效的Python3 - 但不會在Python2.x中工作,因爲您的'thedecorator1'類不會從對象繼承 - 使其成爲「舊式樣類」。許多很好的類機制在舊式類中不起作用 - 描述符,它們是屬性的基礎。你的「財產」聲明在那裏沒有效果。 – jsbueno

回答

0

考慮此程序:

from functools import wraps 

def dec1(fn): 
    @wraps(fn) 
    def wrap(*args, **kw): 
     print "dec1", fn.__name__ 
     return fn(*args, **kw) 
    return wrap 

def dec2(fn): 
    @wraps(fn) 
    def wrap(*args, **kw): 
     print "dec2", fn.__name__ 
     return fn(*args, **kw) 
    return wrap 

class C(object): 
    prop = None 
    @property 
    def foo(self): 
     return self.prop 

    @foo.setter 
    @dec1 
    def foo(self, x): 
     self.prop = x 

class D(C): 
    foo = property(
     C.foo.fget, 
     dec2(C.foo.fset), 
     C.foo.fdel) 

print '.' 
c=C() 
c.foo = 7 
print '.' 
d = D() 
d.foo = 8 
print '.' 

D.foo是繼承C.foo get和刪除功能的屬性,但包裝設定功能。

D另一種定義是:

class D(C): 
    foo = C.foo.setter(dec2(C.foo.fset)) 

在任一種情況下,程序的輸出是:

. 
dec1 foo 
. 
dec2 foo 
dec1 foo 
. 

注意,分配給c.foo調用一個包裝,而分配到d.foo調用兩者。

2

有沒有辦法做到這一點,因爲裝飾並不「知道」之前或之後應用的任何其他裝飾。裝飾器不是附加到函數的額外信息;相反,當你使用裝飾器時,它用替換裝飾版本的原始功能。這不像你有一個單獨的裝飾列表的函數;你只會得到應用所有的裝飾,壓扁成一個對象的最終結果,你不能偷看以後內外知道哪些裝飾都有效。所以,當你寫:

class SomeClass: 
    """That's te base class""" 
    _some_property = None 

    @property 
    @thedecorator2 
    def someproperty(self): 
     return self._some_property 

現在SomeClass.someproperty是一個屬性的對象。該房產對象包裝thedecorator2,但propertythedecorator2不知情;它只是包裝它給定的對象。您定義了一個名爲someproperty的方法,但裝飾器將其包裝並「埋葬」原始函數;有「放鬆」系列的裝飾以獲得訪問原始功能或部分裝飾中間函數(例如,施加thedecorator2但不property的結果)沒有通用的方法。

後來,當你定義AnotherClass,你說你要「增加一個裝飾」。但是,到什麼?您唯一可以訪問的是SomeClass.someproperty。您定義的原始功能不可用;它被「埋藏」在裝飾者之下,你不能把它弄回去。

現在,對於一些修飾器,您可能會得到原始功能退出。例如,property將其吸氣功能存儲在其fget屬性中。但是如何(或者如果!)裝飾器存儲原始函數取決於裝飾器。例如,您的thedecorator2修飾器不會以任何方式暴露func。所以,如果AnotherClass知道到底是哪裝修最初應用,它可能能夠使用大約每一個具體的知識來放鬆他們。但是這仍然需要你知道最初應用了哪些裝飾器,所以它不會在AnotherClass中不需要複製那些信息。

底線是裝飾者不「保存」他們裝飾的原始東西;他們只是把它壓成他們生產的任何東西。沒有通用的方法來「剝離」已裝飾的函數的裝飾器,甚至不知道已經應用了哪些裝飾器。因此,你不能有像insert_new_decorator(somefunction, after=somedecorator)這樣的東西,因爲沒有辦法剝離裝飾層以知道somedecorator在堆棧中。

裝修者不喜歡衣服,在那裏你可以脫掉一件外套,在下面放一件不同的襯衫,然後把原來的外套放回去。相反,想象一下草莓蘸巧克力,然後蘸上鮮奶油,然後淋上冰糖。你不能「打開」奶油和糖衣,並在下面塗上不同的巧克力塗層。整個事物融合成一個物體,其層不能被拉開。

+0

不錯的答案 - 也許你可以添加一個段落,聲明如果在子類中添加的裝飾器可以始終是最外層的(當然,這種方法已經排除了該屬性已經存在),這是可行的,所有這一切需要的是不使用裝飾器語法,而是寫在類體上: 'some_ancestor_method = newdecorator(SomeClass .__ dict __ [「some_ancestor_method」])' – jsbueno