Python 在运行时重写@property getter

Python 在运行时重写@property getter,python,properties,overriding,Python,Properties,Overriding,有没有办法在Python的运行时重写@property getter 我希望我的类有一个“复杂”的getter,它运行一些代码并返回一个可以更改的值。但是,如果出于某种原因,我将该变量设置为其他变量,我希望它始终返回该值 例如: class Random(object): @property def random_number(): return random.randint(0,10) @random_number.setter def ra

有没有办法在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”这样的实例变量来解决这个问题,我总是在getter中签入它,但如果可能的话,我希望避免这样做

理想情况下,我的setter函数可以执行以下操作

self.random_number.getter = lambda:4

但是我不知道如何使它工作。

您不能在实例级别“重写”属性,因为属性是一个同时具有“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似乎更简单。

您可以编写自己的类似属性的类,但不需要使用uuu set\uuu-方法。这样,设置属性将创建实例属性,而不是调用setter方法

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

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

您可以将数字的源定义为一个使用属性设置器切换的函数。这是因为Python支持运动,这意味着您可以在不调用它们的情况下获取对它们的引用,并像其他任何对象一样传递它们:

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

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

那很聪明。我只是不确定聪明在这里是否是件好事。这正是我想要做的,所以我把它标记为被接受。但是,我同意其他人的看法,我将坚持使用简单且不太神奇的覆盖实例属性。