9

__init__デコレータを使用する以外は、さまざまなオブジェクトのすべてのメソッドをラップしたいと思います。

class MyObject(object):

    def method(self):
        print "method called on %s" % str(self)

    @property
    def result(self):
        return "Some derived property"

def my_decorator(func):
    def _wrapped(*args, **kwargs):
        print "Calling decorated function %s" % func
        return func(*args, **kwargs)
    return _wrapped


class WrappedObject(object):

    def __init__(self, cls):
        for attr, item in cls.__dict__.items():
            if attr != '__init__' and (callable(item) or isinstance(item, property)):
                setattr(cls, attr, my_decorator(item))
        self._cls = cls

    def __call__(self, *args, **kwargs):
        return self._cls(*args, **kwargs)

inst = WrappedObject(MyObject)()

ただし、プロパティインスタンスの結果のラッピングは、次のようになります。

@my_decorator
@property
def result(self):
    return "Some derived property"

目的の結果がこれと同等の場合:

@property
@my_decorator
def result(self):
    return "Some derived property"

プロパティオブジェクトの属性は読み取り専用であり、プロパティが関数をラップした後に関数を変更できないようです。私はすでに必要なハッカーのレベルにあまり慣れていないので、とにかくプロパティオブジェクトを掘り下げたくありません。

私が見ることができる他の唯一の解決策は、私が避けたいと思っていたメタクラスをその場で生成することです。明らかな何かが欠けていますか?

4

3 に答える 3

6

このサンプルには他にもいくつかの問題がありますが、疑問を投げかけるために必要なのは、プロパティをラップするときだけです。

プロパティをラップするときは、代わりにその__get__メソッドをラップします。

class MyObject(object):

    def method(self):
        print "method called on %s" % str(self)

    @property
    def result(self):
        return "Some derived property"

    def common(self, a=None):
        print self

def my_decorator(func):
    def _wrapped(*args, **kwargs):
        print "Calling decorated function %s" % func
        return func(*args, **kwargs)
    return _wrapped


class WrappedObject(object):

    def __init__(self, cls):
        for attr, item in cls.__dict__.items():
            if attr != '__init__' and callable(item):
                setattr(cls, attr, my_decorator(item))
            elif  isinstance(item, property):
                new_property = property(my_decorator(item.__get__), item.__set__, item.__delattr__)
                setattr(cls, attr, new_property)
        self._cls = cls

    def __call__(self, *args, **kwargs):
        return self._cls(*args, **kwargs)

inst = WrappedObject(MyObject)()

これは、その仕事をするコードへの最も簡単な変更です。ただし、属性の書き換えを避けるために、ラップしているクラスのサブクラスに動的に変更します。プログラムでサブクラスを作成するには、名前を使用してタイプを調整し、ベースを使用してタプルを作成し、パラメーターとしてdictを使用します。

編集-コードをサブクラスラップクラスに変更

実際、指定されたクラスをサブクラス化するには、指定されたコードをほとんど変更する必要はありませんが、type呼び出しについては指定しました。ここでテストしました-WrappedObjectクラスを次のように変更します。

class WrappedObject(object):

    def __init__(self, cls):
        dct = cls.__dict__.copy()
        for attr, item in dct.items():
            if attr != '__init__' and callable(item):
                dct[attr] =  my_decorator(item)
            elif  isinstance(item, property):
                new_property = property(my_decorator(item.__get__), item.__set__, item.__delattr__)
                dct[attr] = new_property
        self._cls = type("wrapped_" + cls.__name__, (cls,), dct)

    def __call__(self, *args, **kwargs):
        return self._cls(*args, **kwargs)
于 2011-02-07T17:16:26.563 に答える
2

少し試行錯誤した結果、次のような解決策を思いつきました。まず、装飾された記述子をエミュレートするヘルパークラスを作成します。

class DecoratedDescriptor(object):

    def __init__(self, descriptor, decorator):
        self.funcs = {}
        for attrname in '__get__', '__set__', '__delete__':
            self.funcs[attrname] = decorator(getattr(descriptor, attrname))

    def __get__(self, *args, **kwargs):
        return self.funcs['__get__'](*args, **kwargs)

    def __set__(self, *args, **kwargs):
        return self.funcs['__set__'](*args, **kwargs)

    def __delete__(self, *args, **kwargs):
        return self.funcs['__delete__'](*args, **kwargs)

次に、プロパティが表示されたら、それをラップします。

# Fragment of your WrappedObject.__init__ method:
if attr != '__init__' and callable(item):
    setattr(cls, attr, my_decorator(item))
elif isinstance(item, property):
    setattr(cls, attr, DecoratedDescriptor(item, my_decorator))

これは次のように機能します。

>>> inst = WrappedObject(MyObject)()
>>> print inst.result
Calling decorated function <method-wrapper '__get__' of property object at 0x00BB6930>
Some derived property

三!メタクラスなし:)

于 2011-02-07T16:59:18.040 に答える
0

たとえば、独自のデコレータの後に適用される「レイジー」デコレータを導入できます。

class Lazy(object):
    def __init__(self, decorator):
        self.decorator = decorator
    def __call__(self, method):
        self.method = method
        return self

def my_decorator(func):
    def _wrapped(*args, **kwargs):
        print "Calling decorated function %s" % func
        return func(*args, **kwargs)
    if isinstance(func, Lazy):
        lazy = func
        func = lazy.method
        return lazy.decorator(_wrapped)
    return _wrapped

lazy_property = Lazy(property)

..そして、@lazy_propertyの代わりに使用し@propertyます。(警告:テストされていないコードですが、アイデアが得られることを願っています...)

于 2011-02-07T16:59:31.817 に答える