2016-04-15 78 views
3

我有兩個類AB,其中BA繼承並覆蓋屬性。 A不在我的控制之下,所以我無法更改它。在類內部和外部呼叫者之間進行區分

的代碼如下:

class A(): 
    def __init__(self, value): 
     self.value = value 
    @property 
    def value(self): 
     return self._value 
    @value.setter 
    def value(self, value): 
     self._value = value 

class B(A): 
    def __init__(self, value): 
     super(B, self).__init__(value) 
    @property 
    def value(self): 
     return super(B, self).value 
    @value.setter 
    def value(self, value): 
     raise AttributeError("can't set attribute") 

當我打電話B(1)我明明得到AttributeError: can't set attribute

我想有不同的行爲時value從內部類的方法

@value.setter 
def value(self, value): 
    if set from inside class hierarchy: 
     pass 
    else: 
     raise AttributeError("can't set attribute") 

模塊inspect似乎並沒有給我足夠的信息來做到這一點,除了對已知函數列表檢查組。

+4

你就不能設置'self._value',而不是'self.value'?屬性作爲外部世界的API存在,不一定在對象本身內。 – acdr

+0

您可以檢查堆棧跟蹤以找出調用者是誰。 – Norman

+0

@acdr,不幸的是'A'不在我的控制之下,所以我不能改變它的行爲方式。我編輯了這個問題來反映這一點。 – Dumitru

回答

0

您可以檢查堆棧,以確定誰調用,不管它的類層次來決定是否還是不要使其:

import inspect 


def who_called(): 
    frame = inspect.stack()[2][0] 
    if 'self' not in frame.f_locals: 
     return None, None 
    cls = frame.f_locals['self'].__class__ 
    method = frame.f_code.co_name 
    return cls, method 

class A(object): 
    def __init__(self, value): 
     self._value = value 

    @property 
    def value(self): 
     return self._value 

    @value.setter 
    def value(self, value): 
     self._value = value 

    # Assuming this existed it would also work 
    def change_value(self, value): 
     self.value = value 

B現在檢查:

class B(A): 
    def __init__(self, value): 
     super(B, self).__init__(value) 

    @property 
    def value(self): 
     return super(B, self).value 

    @value.setter 
    def value(self, value): 
     cls, method = who_called() 
     if cls in B.__mro__ and method in A.__dict__: 
      self._value = value 
     else: 
      raise AttributeError("can't set attribute") 

證明:

b = B('does not raise error') 
b.change_value('does not raise error') 
b.value = 'raises error' 
0

您可以使用調用的代碼來確定調用是否來自類內部。如果通話沒有以self.value =開頭,則只發出異常。

import re 
import traceback 

class A(object): 
    def __init__(self, value): 
     self.value = value 
    @property 
    def value(self): 
     return self._value 
    @value.setter 
    def value(self, value): 
     self._value = value 

class B(A): 
    def __init__(self, value): 
     super(B, self).__init__(value) 
    @property 
    def value(self): 
     return super(B, self).value 
    @value.setter 
    def value(self, value): 
     call = traceback.extract_stack(limit=2)[0][3] 
     if re.match(r'self.value\s*=', call): 
      pass 
     else: 
      raise AttributeError("can't set attribute") 

b = B(1) # OK 
b.value = 3 # Exception 

當然,這個只要你開始呼叫你的變量self打破:

self = B(1) # OK 
self.value = 3 # Meh, doesn't fail 
相關問題