Python 在@property之后修饰类方法

Python 在@property之后修饰类方法,python,metaclass,decorator,Python,Metaclass,Decorator,我想使用装饰器包装各种对象的每个方法,除了\uuuu init\uuuu 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(

我想使用装饰器包装各种对象的每个方法,除了
\uuuu init\uuuu

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"
当所需结果与此等效时:

@my_decorator
@property
def result(self):
    return "Some derived property"
@property
@my_decorator
def result(self):
    return "Some derived property"
属性对象的属性似乎是只读的,防止在属性包装函数后对其进行修改。我对已经要求的黑客级别不太满意,我也不想深入研究属性对象


我能看到的唯一其他解决方案是动态生成一个元类,这是我希望避免的。我是否遗漏了一些明显的东西?

经过一番尝试和错误,我想出了以下解决方案。首先,创建一个将模拟修饰描述符的帮助器类:

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
inst=WrappedObject(MyObject)() >>>打印安装结果 调用修饰函数 某些派生性质 那里!没有元类:)

您可以引入“惰性”装饰器,它们在您自己的装饰器之后应用,例如:

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
。(警告:未经测试的代码,但我希望您能理解…

此示例中还有一些其他问题,但要回答这个问题,您所要做的就是 是,当您包装属性时

包装属性时,请改为包装其_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作为参数来调整类型

编辑-将代码更改为子类包装类 实际上,子类化给定的类几乎不需要修改给定的代码, 但是对于我所指示的
类型
调用。我刚刚在这里进行了测试-将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)

在这段代码中,当运行wrappedobject的init修改原始类后,您是否发现它不再具有非包装版本?我同意创建子类会更整洁,我宁愿避免对原始类进行猴子补丁。