2012-11-15 48 views
9

我想實現一個裝飾器類,它將裝飾其他類中的方法。但是,我需要在裝飾器中保存裝飾方法的類。我似乎無法在任何地方找到它。如何在Python中找到裝飾方法的包含類

下面是一個例子:

class my_decorator(object): 

    def __init__(self, arg1, arg2): 
    print(self.__class__.__name__ + ".__init__") 
    self.arg1 = arg1 
    self.arg2 = arg2 

    def __call__(self, my_callable): 
    print(self.__class__.__name__ + ".__call__") 
    print(type(my_callable)) 
    self.my_callable = my_callable 
# self.my_callable_method_class = ?where to get this? 

    def function_wrapper(*args, **kwargs): 
     print(self.__class__.__name__ + ".function_wrapper") 
     print(self.arg1) 
     self.my_callable.__call__(*args, **kwargs) 
     print(self.arg2) 

    return function_wrapper 


class MyClass(object): 

    @my_decorator(arg1="one", arg2="two") 
    def decorated_method(self): 
    print(self.__class__.__name__ + ".decorated_method") 
    print(type(self.decorated_method)) 
    print("hello") 


m = MyClass() 
m.decorated_method() 

,將打印出來的:

my_decorator.__init__ 
my_decorator.__call__ 
<type 'function'> 
my_decorator.function_wrapper 
one 
MyClass.decorated_method 
<type 'instancemethod'> 
hello 
two 

在裝飾類的調用是類型的函數,而類本身裏面的類型是instancemethod的。我可以從instancemethod中獲取im_class,但函數中沒有這樣的東西。

如何從裝飾器中獲取包含裝飾方法的類?

我可以這樣做:

class my_decorator(object): 

    def __init__(self, cls, arg1, arg2): 

. 
. 

class MyClass(object): 

    @my_decorator(cls=MyClass, arg1="one", arg2="two") 
    def decorated_method(self): 

. 
. 

但我不喜歡這樣做,因爲它是多餘的,而不是漂亮的。

或者我應該用其他方式來實現嗎?我基本上需要一些裝飾器的參數,我需要裝飾器中的裝飾方法的類。

+0

你到底什麼時候需要班級?您可以將內部包裝修改爲'def function_wrapper(self,* args,** kwargs)'並將該類作爲'self .__ class__'獲得。如果你需要修飾者以外的課程,那麼katrielalex已經指出了它的難度。 – Bakuriu

回答

3

你可以裝點

@decorate 
class MyClass(object): 

    @my_decorator(arg1="one", arg2="two") 
    def decorated_method(self): 

,並使用外裝飾派類參數內。


您的建議都不能正常工作,因爲他們需要的類訪問它的存在之前。當你定義一個類時,你首先在它的主體內部執行代碼(定義函數等),然後將結果作用域作爲__dict__分配給類。因此,在定義decorated_method時,MyClass尚不存在。

+0

這看起來像要走的路。但是,我如何鏈接類裝飾器和方法裝飾器?我會有很多類和方法使用相同的裝飾器... – kortsi

+0

'@ my_decorator'應該標記它裝飾的方法,比如說給它們一個特殊的屬性。 '@ decorate'應該迭代所有的類方法,過濾沒有特殊屬性的類,然後依次對它們進行修飾。 – katrielalex

+0

所以它似乎工作。謝謝! – kortsi

0

如果您使裝飾器返回的對象爲descriptor,那麼您可以掛鉤屬性查找以返回鏈接方法和類(或實例)的其他對象。

對於方法樣式描述符,您只需實現__get__方法。當查找類上的方法,下面兩個是等價的:

m = MyClass.decorated_method 
# It will actually get the object from any parent class too. But this will do for a simple example 
m = MyClass.__dict__['decorated_method'].__get__(MyClass) 

併爲實例,下面是等價的:

instance = MyClass() 
m = instance.decorated_method 
m = type(instance).__dict__['decorated_method'].__get__(instance, type(instance)) 

所以表達式instance.decorated_method(...)實際調用你的__get__返回的對象方法。這是允許簡單的函數對象變成綁定的方法對象的相同過程,這些對象添加了隱含的參數self

創建此可調用對象時,您應該擁有所需的所有信息。

+0

這個問題是我在創建第一個實例之前需要和類一起工作。 – kortsi

+0

夠公平的。然後你可能會需要一個裝飾器來標記katrielalex建議的函數,然後從類裝飾器或元類構造器中修復它們。 –

1

這是修改後的版本。

# This holds all called method_decorators 
global_method_decorator_list = [] 

class class_decorator(object): 
    def __init__(self, arg1, arg2): 
    print(self.__class__.__name__ + ".__init__") 
    self.arg1 = arg1 
    self.arg2 = arg2 

    def __call__(self, my_class): 
    print(self.__class__.__name__ + ".__call__") 
    print(repr(my_class)) 
    print(my_class.__name__) 
    self.cls = my_class 
    class_decorators[my_class] = self 
    self.my_class = my_class 

    # Call each method decorator's second_init() 
    for d in global_method_decorator_list: 
     d._method_decorator_.second_init(self, my_class) 

    def wrapper(*args, **kwargs): 
     print(self.__class__.__name__ + ".wrapper") 
     print(self.arg1) 
     retval = self.my_class.__call__(*args, **kwargs) 
     print(self.arg2) 
     return retval 

    return wrapper 


class method_decorator(object): 
    def __init__(self, arg1, arg2): 
    print(self.__class__.__name__ + ".__init__") 
    self.arg1 = arg1 
    self.arg2 = arg2 

    def __call__(self, my_callable): 
    print(self.__class__.__name__ + ".__call__") 
    print(repr(my_callable)) 
    self.my_callable = my_callable 

    # Mark the callable and add to global list 
    my_callable._method_decorator_ = self 
    global_method_decorator_list.append(my_callable) 

    def wrapper(*args, **kwargs): 
     print(self.__class__.__name__ + ".wrapper") 
     print(self.arg1) 
     retval=self.my_callable.__call__(*args, **kwargs) 
     print(self.arg2) 
     return retval 

    return wrapper 

    def second_init(self, the_class_decorator, the_class): 
    print(self.__class__.__name__ + ".second_init") 
    print("The Class: " + repr(the_class))** 


@class_decorator(arg1="One", arg2="Two") 
class MyClass(object): 

    @method_decorator(arg1="one", arg2="two") 
    def decorated_method(self): 
    print(self.__class__.__name__ + ".decorated_method") 
    print(type(self.decorated_method)) 
    print("hello") 


m = MyClass() 
m.decorated_method() 

輸出看起來是這樣的:

class_decorator.__init__ 
method_decorator.__init__ 
method_decorator.__call__ 
<function decorated_method at 0x3063500> 
class_decorator.__call__ 
<class '__main__.MyClass'> 
MyClass 
method_decorator.second_init 
The Class: <class '__main__.MyClass'> 
class_decorator.wrapper 
One 
Two 
method_decorator.wrapper 
one 
MyClass.decorated_method 
<type 'instancemethod'> 
hello 
two 

所不同的是,現在是類獨立的裝飾。類裝飾器的調用()將調用每個方法裝飾器「second_init()」方法,並在那裏傳遞類。

有意思的是,method_decorator的調用()將在class_decorator之前調用。