2013-04-26 22 views
11

I love Pythonの@propertyデコレートシステム。 aClassObect.attributeに電話するときにカスタムコードを実行できることが大好きです。特に、属性を設定しているときにデータを検証する場合。しかし、存在しない属性を設定しようとしたときにカスタムコードを実行する方法は、私が欲しいものの、見つけられないものです。クラス内の未定義/未実装属性のPython "catch all"メソッド

class C(object): 
    def __init__(self): 
     self._x = None 

    @property 
    def x(self): 
     """I'm the 'x' property.""" 
     return self._x 

    @x.setter 
    def x(self, value): 
     self._x = value 

    @x.deleter 
    def x(self): 
     del self._x 

、私はクラスCのインスタンスであるmyObjを、持っている、と私はmyObject.x = 42を呼び出す場合、それが最適です適切なセッターを、実行します:たとえば、私は次のクラスを持っていると言いますデータの検証。しかし、これは誰かがmyOjbect.b = 47を呼び出すのを止めず、bという新しい属性をうれしく作成します。新しい属性を設定するときに特別なコードを実行する方法はありますか?私は "エラー、この属性が存在しない"のようなものを言うためにエラーを発生させる能力を持っていました。

+2

私はデータベースのAPIとして機能するクラスを持っています。各インスタンスはエントリを表します。データベースのフィールドを表すオブジェクトの属性を変更した後、 'object.save()'を呼び出してDBを更新します。実際にはDBに存在しないため、フィールドを静かにドロップするのではなく、むしろエラーを発生させたいと思います。 –

答えて

9

Elazarの答えを少し詳しく説明するには、__setattr__のマジックメソッドをオーバーライドして、その属性がすでに存在するかどうかチェックしてください。そうでない場合は、例外を発生させます。ここではそれがあなたのクラスのように見えることができるものです:

def __setattr__(self, name, value): 
    if not hasattr(self, name): # would this create a new attribute? 
     raise AttributeError("Creating new attributes is not allowed!") 
    super(C, self).__setattr__(name, value) 

それはあなたが存在しない属性の要求に対処したい場合は__getattr__または__getattribute__を上書きすることも可能ですが、あなたはこれを説明している特定の問題のために、おそらくではありません必要。

上記の__setattr__のメソッドは、Deleterのためにあなたの現在のプロパティで完全にうまく動作しないことに注意してください。 myObj.xを削除すると、後でxにアクセスしようとすると、getterによって例外が発生します。これは、xが既存の属性であるかどうかを確認するときにhasattrFalseを返すことを意味します。したがって、__getattr__は再作成できません。特定の属性を実際に削除(再作成)する必要がある場合は、より洗練された__setattr__の実装が必要です(hasattrに頼るのではなく、希望の名前のプロパティをクラス内でチェックできます)。

+0

ああ待って、私はこれがうまくいくと確信していましたが、キャッチがあります:この制限は、値を初期化するときにコンストラクタを含むどこにでも適用されます。私はそれを試したところ、コンストラクタの 'AttributeError'で私を止めました。これを回避する方法はありますか? –

+0

@ J-bob:ああ、そうです。 'super(C、self).__ setattr __(" _ x "、None)'のようなものを明示的に呼び出す必要があります。 – Blckknght

+0

良い解決策を見つけました。私のファイルの一番上に、私は輸入検査をします。そして、私はあなたの行を 'hasattr(self、name):'ではなく 'hasattr(self、name)とinspect.stack()[1] [3]!= '__init __':'と置き換えます。 'inspect.stack'は呼び出しスタックを調べてみましょう。したがって、このコードはそれを呼び出すメソッドの名前をチェックします。呼び出し元のメソッドが '__init__'の場合、属性を作成することができます。それ以外の場合は、エラーが発生します。これを表示するためにあなたの答えを変更することをお勧めします。 –

9

__getattr__および__setattr__を無効にします。