2012-11-15 7 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でありながら

は、ここでの例です。私はinstance_ethodから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__メソッドを実装する必要があります。クラスにメソッドを検索するときは、次の2つは等価です:

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の前に呼び出されることに注意してください。