如何使用方法有条件地覆盖Python类属性

时间:2012-10-08 02:31:07

标签: python class

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

如果我有这个类,我可以通过传入dict来实例化它:

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设置为''None

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

然后我调用bar属性:

my_foo.bar

并获取

u"I have overridden foo's bar"

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

我能以某种方式这样做吗?任何帮助都会很棒。

2 个答案:

答案 0 :(得分: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

答案 1 :(得分:0)

要覆盖属性,您必须对该类执行操作,而不是对实例执行操作,因为实例上的机制在__dict__查找之前被调用,并且您最终得到AttributeError s。相反,您可以在班级上设置不同的属性。

但要这样做,你必须在每次创建实例时修改你的类(我打赌你不想要),或者你必须动态生成新的类。

例如:

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

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