2016-04-15 15 views
3

ABの2つのクラスがあります。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") 

私は明らかにAttributeError: can't set attributeを得るB(1)を呼び出すしようとします。私はvalueがモジュールinspectが知られている機能のリストに対してチェックを除いて、私はこれを行うに十分な情報を提供していないようですクラスメソッド

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

内から設定されているときに、異なる振る舞いを持ちたい

+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 
関連する問題