有没有办法在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
但我不知道如何做到这一点。
答案 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._number
,None
表示没有强制进行过任何变量),只要你真的必须实现这种愚蠢和令人困惑的行为。
答案 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)
并返回结果。