2014-05-08 70 views
0

有沒有一種方法可以在Python中重寫運行時的@property getter?在運行時覆蓋@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 

但我不知道如何使這項工作。

回答

4

你可以編寫自己的類似財產的課程,但沒有__set__-方法。這樣,設置屬性將創建一個實例屬性,而不是調用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 
+1

這很聰明。我只是不確定在這裏「聰明」是否是件好事。 – delnan

+0

這正是我想要做的,所以我正在接受。但是,我同意其他人的觀點,我只會堅持簡單而不那麼麻煩的'_override'實例屬性。 –

5

由於屬性爲data descriptor(它同時具有__get____set__掛鉤),因此無法在實例級別上「覆蓋」某個屬性。

這意味着您的獲取者已有來檢查實例屬性。

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似乎更簡單。

0

您可以在運行時替換屬性。實際上,即使是定義屬性並將它附加到類中的行爲也是一個運行時操作(在Python運行時沒有發生值得注意的事情)。但是,由於屬性是描述符,描述符是的一部分,因此它不適合存儲每個實例的數據。你應該仍然使用一個單獨的變量(也許self._numberNone表示還沒有被強制),前提是你必須實現這個愚蠢而混亂的行爲。

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 

見的文檔functools.partial的詳細信息在partial(random.randint, 0, 10)。它基本上創建一個函數,當被調用時,調用random.randint(0, 10)並返回結果。