在运行时覆盖@property getter

时间:2014-05-08 21:05:47

标签: python properties override

有没有办法在Python中覆盖运行时的@property getter?

我希望我的班级有一个复杂的' getter,运行一些代码并返回一个可以改变的值。但是,如果由于某种原因我将该变量设置为其他变量,我希望它始终返回该值。

例如:

class Random(object):
    @property
    def random_number():
        return random.randint(0,10)


    @random_number.setter
    def random_number(value):
        # Overlord says that random must always return `value`
        (((I don't know that to do here)))


>>>r = Random()
>>>r.random_number
>>>(returns something between 0,10)

>>>r.random_number = 4 # Confirmed by dice roll
>>>r.random_number
>>>4 (always)

我知道我可以使用像' force_value'这样的实例var,我总是检查我的getter,但我想尽可能避免这种情况。

理想情况下,我的setter函数可以执行类似

的操作
self.random_number.getter = lambda:4

但我不知道如何做到这一点。

4 个答案:

答案 0 :(得分:5)

你无法覆盖'实例级别的属性,因为属性是data descriptor(它同时包含__get____set__个钩子。

这意味着你的getter 来检查实例属性。这并不像听起来那么痛苦:

class Random(object):
    @property
    def random_number(self):
        if hasattr(self, '_override'):
            return self._override
        return random.randint(0,10)

    @random_number.setter
    def random_number(self, value):
        # Overlord says that random must always return `value`
        self._override = value

您甚至可以添加删除器:

    @random_number.deleter
    def random_number(self):
        if hasattr(self, '_override'):
            del self._override

另一种方法是创建自己的替代描述符类,它不会实现__setter__挂钩,这样您就可以直接在实例上设置属性并使其覆盖描述符。但对于您的用例,只使用显式setter似乎更简单。

答案 1 :(得分:4)

您可以编写自己的类似属性的类,但不使用__set__方法。这样,设置属性会创建实例属性,而不是调用setter-method

import random

class property_getter(object):
    def __init__(self, getter):
        self.getter = getter

    def __get__(self, obj, cls):
        return self.getter(obj)

class Random(object):
    @property_getter
    def random_number(self):
        return random.randint(0,10)

x = Random()
print x.random_number
print x.random_number
x.random_number = 5
print x.random_number

答案 2 :(得分:0)

您可以在运行时替换属性。实际上,即使是定义属性并将其附加到类的行为也是一个运行时操作(在Python中运行时没有任何值得注意的事情发生)。但是,由于属性是描述符而描述符是的一部分,因此它不是存储每个实例数据的正确位置。你仍然应该使用一个单独的变量(可能是self._numberNone表示没有强制进行过任何变量),只要你真的必须实现这种愚蠢和令人困惑的行为。

答案 3 :(得分:0)

您可以将数字的来源定义为使用属性设置器切换的函数。这是因为Python运动first-class functions,这意味着你可以获得对它们的引用(不调用它们)并像任何其他对象一样传递它们:

from functools import partial
import random


class Random(object):
    def __init__(self):
        self.number_source = partial(random.randint, 0, 10)

    @property
    def random_number(self):
        return self.number_source()

    @random_number.setter
    def random_number(self, value):
        self.number_source = lambda: value


r = Random()
print r.random_number

r.random_number = 42
print r.random_number

输出:

<random int>
42

有关partial(random.randint, 0, 10)的详细信息,请参阅functools.partial的文档。它基本上创建了一个函数,在调用时调用random.randint(0, 10)并返回结果。