在@property之后装饰一个类方法

Chi*_*ggs 8 python metaclass decorator

我想包装各种对象的每个方法,除了__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)()
Run Code Online (Sandbox Code Playgroud)

但是,包装属性实例结果等同于:

@my_decorator
@property
def result(self):
    return "Some derived property"
Run Code Online (Sandbox Code Playgroud)

当期望的结果与此相当时:

@property
@my_decorator
def result(self):
    return "Some derived property"
Run Code Online (Sandbox Code Playgroud)

似乎属性对象的属性是只读的,以防止在属性包装后修改函数.我对已经要求的hackery水平不太满意,而且我还是不想深入研究属性对象.

我能看到的唯一其他解决方案是动态生成一个我希望避免的元类.我错过了一些明显的东西吗

jsb*_*eno 5

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

包装属性时,请包装其__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)()
Run Code Online (Sandbox Code Playgroud)

那就是对完成这项工作的代码的简单修改。但是,为了避免重新编写其属性,我将其更改为包装的类的子类。您可以通过编程简单地创建一个带有名称的类型,带有基数的元组和以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)
Run Code Online (Sandbox Code Playgroud)