简体   繁体   English

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

[英]How to conditionally override a Python class property with a method

Is it possible to conditionally override a class property with a property method? 是否可以使用属性方法有条件地覆盖类属性?

If I have this class which I can instantiate by passing in a dict: 如果我有这个类,可以通过传递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"

And I make this instance and set bar to '' or None : 然后我创建此实例并将bar设置为''None

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

and then I call the bar property: 然后调用bar属性:

my_foo.bar

and get 并得到

u"I have overridden foo's bar"

I would like to have the property method bar returned, instead of the bar value that was passed in when the object was created. 我想返回属性方法bar ,而不是创建对象时传递的bar值。

Can I do that somehow? 我能以某种方式做到吗? Any help would be awesome. 任何帮助都是极好的。

Python properties can't be overwritten, as doing so raises an AttributeError . Python属性不能被覆盖,因为这样做会引发AttributeError However, you could try storing your override values and then looking them up when the property is executed: 但是,您可以尝试存储您的替代值,然后在执行属性时查找它们:

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

To override a property you have to act on the class and not on the instance, because their machinery, on the instance, gets called before __dict__ lookup and you end up with AttributeError s. 要覆盖属性,您必须在类上而不是实例上起作用,因为在实例上调用__dict__之前,将调用它们的机制,最后得到AttributeError Instead you can set a different property on the class. 相反,您可以在类上设置其他属性。

But to do so you either have to modify your class every time you create an instance(which I bet you do not want), or you have to generate new classes dynamically. 但是要这样做,您要么在每次创建实例时都要修改类(我敢打赌您不希望这样做),要么必须动态生成新类。

For example: 例如:

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)

And the results are: 结果是:

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

I don't like much this kind of hacks. 我不太喜欢这种骇客。 Probably the easier way of doing thing is calling a private method that computes the property value, for example: 也许做事的更简单方法是调用一个计算属性值的私有方法,例如:

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

Which yields the same results as before: 产生与以前相同的结果:

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

I believe this trick could be seen as an application of the Template Method design pattern, even though it is applied to properties. 我相信,即使将技巧应用于属性,也可以将其视为模板方法设计模式的一种应用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM