2

是否可以使用属性方法有条件地覆盖类属性?

如果我有这个类,我可以通过传入一个字典来实例化:

def Foo(Object):
    def __init__(self, **kwargs):
        self.__dict__.update(kwargs)

    # pseudo code (this doesn't work)
    if not self.bar:
        @property
        def bar(self):
            return u"I have overridden foo's bar"

我创建了这个实例并设置bar''or None

my_foo = Foo(**{'bar':u''})

然后我调用该bar属性:

my_foo.bar

并得到

u"I have overridden foo's bar"

我希望bar返回属性方法,而不是bar创建对象时传入的值。

我能以某种方式做到这一点吗?任何帮助都是极好的。

4

2 回答 2

0

要覆盖一个属性,您必须对类而不是实例进行操作,因为它们在实例上的机器在__dict__查找之前被调用,并且最终得到AttributeErrors。相反,您可以在类上设置不同的属性。

但要做到这一点,您要么必须在每次创建实例时修改类(我敢打赌你不想要),要么必须动态生成新类。

例如:

class Foo(object):
    def __init__(self, val):
        self._val = val
    @property
    def val(self):
        return self._val

class SubType(Foo):
    def __new__(cls, val):
        if val % 2:
            #random condition to change the property
            subtype = type('SubFoo', (SubType,),
                           {'val': property((lambda self: self._val + 1))})
                return object.__new__(subtype)
            else:
                return object.__new__(cls)

结果是:

>>> d = SubType(3)  #property changed
>>> d.val
4
>>> f = SubType(2)  #same property as super class
>>> f.val
2

我不太喜欢这种黑客行为。可能更简单的方法是调用计算属性值的私有方法,例如:

class Foo(object):
    def __init__(self, val):
        self._val = val
    def _compute_val(self):
        return self._val
    @property
    def val(self):
        return self._compute_val()

class SubFoo(Foo):
    def _compute_val(self):
        if self._val % 2:
                return self._val + 1
        else:
                return self._val

产生与以前相同的结果:

>>> d = SubFoo(3)
>>> d.val
4
>>> f = SubFoo(2)
>>> f.val
2

我相信这个技巧可以被视为模板方法设计模式的应用,即使它应用于属性。

于 2012-10-09T05:55:39.113 回答
0

Python 属性不能被覆盖,因为这样做会引发AttributeError. 但是,您可以尝试存储覆盖值,然后在执行属性时查找它们:

def overridable(of):
    def nf(self):
        if hasattr(self, "_overrides") and of.__name__ in self._overrides:
            return self._overrides[of.__name__]
        return of(self)
    return nf


class Foo(object):
    _overrides = {}

    def __init__(self, **kwargs):
        for k, v in kwargs.iteritems():
            try:
                setattr(self, k, v)
            except AttributeError:
                self._overrides[k] = v

    @property
    @overridable
    def bar(self):
        return u'I have overridden foo\'s bar'


my_foo = Foo(bar=3)
print my_foo.bar
于 2012-10-08T02:44:08.223 回答