Pythonの@ foo.setterが機能しないのはなぜですか?


155

そのため、Python 2.6でデコレーターを使用していますが、デコレーターを機能させるのに問題があります。これが私のクラスファイルです:

class testDec:

    @property
    def x(self): 
        print 'called getter'
        return self._x

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

これはxプロパティのように扱うことですが、getおよびset時にこれらの関数を呼び出します。そこで、IDLEを起動して確認しました。

>>> from testDec import testDec
from testDec import testDec
>>> t = testDec()
t = testDec()
>>> t.x
t.x
called getter
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "testDec.py", line 18, in x
    return self._x
AttributeError: testDec instance has no attribute '_x'
>>> t.x = 5
t.x = 5
>>> t.x
t.x
5

明らかに最初の呼び出しは期待どおりに機能します。これは、getterを呼び出しており、デフォルト値がないため失敗します。はい、わかりました。ただし、assignを呼び出すとt.x = 5新しいプロパティが作成されるようでx、ゲッターが機能しなくなります。

何が欠けていますか?


6
クラスは大文字で名前を付けてください。変数とモジュールファイルには小文字を使用できます。
S.Lott、2009

回答:


306

あなたはPython 2で古典的な古いスタイルのクラスを使用しているようです。プロパティが正しく機能するためには、代わりに新しいスタイルのクラスを使用する必要があります(Python 2では、から継承objectする必要があります)。クラスをMyClass(object)次のように宣言してください:

class testDec(object):

    @property
    def x(self): 
        print 'called getter'
        return self._x

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

できます:

>>> k = testDec()
>>> k.x
called getter
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/devel/class_test.py", line 6, in x
    return self._x
AttributeError: 'testDec' object has no attribute '_x'
>>> k.x = 5
called setter
>>> k.x
called getter
5
>>> 

問題を引き起こす可能性のあるもう1つの詳細は、プロパティが機能するために両方のメソッドに同じ名前が必要であることです。このような別の名前でセッターを定義した場合、機能しません

@x.setter
def x_setter(self, value):
    ...

また、最初に見つけるのが簡単ではないもう1つのことは、順序です。ゲッターを最初に定義する必要があります。最初にセッターを定義すると、name 'x' is not definedエラーが 発生します。


4
Python3では、これ以上は必要ありません-「クラシック」クラスはセッターで問題ありません:-)
Eenoku

20
すべてのクラスは新しいスタイルのクラスであり、「クラシック」クラスはもう存在しないため、Python 3で機能します。
2015年

この場合、デコレータが正しく処理されない場合、警告/エラーが表示されるはずです。
stfn

82

この例外を探してここでつまずく他の人々のためのちょうどメモ:両方の関数は同じ名前を持つ必要があります。メソッドに次のように名前を付けると、例外が発生します。

@property
def x(self): pass

@x.setter
def x_setter(self, value): pass

代わりに、両方のメソッドに同じ名前を付けます

@property
def x(self): pass

@x.setter
def x(self, value): pass

宣言の順序が重要であることにも注意することが重要です。ゲッターは、ファイル内のセッターの前に定義する必要があります。そうしないと、NameError: name 'x' is not defined


これはおそらく、「新しい」ベストアンサーになるでしょう
。Python3を使用する

5
この答えは素晴らしいです。完全であるためには、順序が重要であること、つまりゲッターがコード内のセッターの前になければならないことを述べていただければすばらしいと思います。そうしないと、NameError: name 'x' is not defined例外が発生します。
eguaio

これをありがとう。これで一日の最高の部分を無駄にしました。メソッドを同じものと呼ばなければならないということは、私には思いもしなかった。本当にイライラする小さなイディオム!
ajkavanagh

この答えの背後にある「理由」を理解するには、こちらの決定的なドキュメントをご覧ください:docs.python.org/2/library/functions.html#property特に「完全に同等」の部分に注意してください。
Evgeni Sergeev 2018年

23

オブジェクトからクラスを派生させる新しいスタイルのクラスを使用する必要があります。

class testDec(object):
   ....

その後、動作するはずです。


上記のすべてが整っていました。これは私がpython 2.7.xでクラスのプロパティを適切にキックするために必要でした。
ドローンブレイン

12

誰かがグーグルからここに来た場合、上記の回答に加えて、この回答に__init__基づいてクラスのメソッドからセッターを呼び出すときに注意が必要であることを付け加えたいと思います 具体的に:

class testDec(object):                                                                                                                                            

    def __init__(self, value):
        print 'We are in __init__'
        self.x = value # Will call the setter. Note just x here
        #self._x = value # Will not call the setter

    @property
    def x(self):
        print 'called getter'
        return self._x # Note the _x here

    @x.setter
    def x(self, value): 
        print 'called setter'
        self._x = value # Note the _x here

t = testDec(17)
print t.x 

Output:
We are in __init__
called setter
called getter
17

__init__メソッドからだけでなく、クラス内の他のすべてのメソッドから。
Mia
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.