回答:
これを試してください:Pythonプロパティ
サンプルコードは次のとおりです。
class C(object):
def __init__(self):
self._x = None
@property
def x(self):
"""I'm the 'x' property."""
print("getter of x called")
return self._x
@x.setter
def x(self, value):
print("setter of x called")
self._x = value
@x.deleter
def x(self):
print("deleter of x called")
del self._x
c = C()
c.x = 'foo' # setter called
foo = c.x # getter called
del c.x # deleter called
._x
(プロパティではなく、単なる属性)への参照は、property
ラッピングをバイパスします。を.x
通過する参照のみproperty
。
ゲッターとセッターを使用するpythonicの方法は何ですか?
「Pythonic」の方法は、「ゲッター」と「セッター」を使用するのではなく、質問が示すようにプレーンな属性を使用しdel
、削除することです(ただし、名前は無害な...ビルトインを保護するために変更されます)。
value = 'something'
obj.attribute = value
value = obj.attribute
del obj.attribute
後で設定を変更して取得したい場合は、property
デコレータを使用することで、ユーザーコードを変更せずに行うことができます。
class Obj:
"""property demo"""
#
@property # first decorate the getter method
def attribute(self): # This getter method name is *the* name
return self._attribute
#
@attribute.setter # the property decorates with `.setter` now
def attribute(self, value): # name, e.g. "attribute", is the same
self._attribute = value # the "value" name isn't special
#
@attribute.deleter # decorate with `.deleter`
def attribute(self): # again, the method name is the same
del self._attribute
(各デコレータの使用法は、以前のプロパティオブジェクトをコピーおよび更新するため、各セット、取得、および削除の関数/メソッドには同じ名前を使用する必要があることに注意してください。
上記を定義した後、元の設定、取得、および削除コードは同じです。
obj = Obj()
obj.attribute = value
the_value = obj.attribute
del obj.attribute
これは避けてください:
def set_property(property,value): def get_property(property):
まず、プロパティが(通常はself
)に設定されるインスタンスに次のような引数を指定しないため、上記は機能しません。
class Obj:
def set_property(self, property, value): # don't do this
...
def get_property(self, property): # don't do this either
...
第二に、これは二つの特別なメソッドの目的を複製し、__setattr__
そして__getattr__
。
3番目に、setattr
およびのgetattr
組み込み関数もあります。
setattr(object, 'property_name', value)
getattr(object, 'property_name', default_value) # default is optional
@property
デコレータはゲッターとセッターを作成するためです。
たとえば、設定動作を変更して、設定される値に制限を設けることができます。
class Protective(object):
@property
def protected_value(self):
return self._protected_value
@protected_value.setter
def protected_value(self, value):
if acceptable(value): # e.g. type or range check
self._protected_value = value
一般に、property
直接の属性は使用せず、そのまま使用します。
これはPythonのユーザーが期待することです。最小のサプライズのルールに従って、反対の非常に説得力のある理由がない限り、ユーザーが期待することをユーザーに提供するようにしてください。
たとえば、オブジェクトの保護された属性が0から100までの整数で、削除されないようにする必要があり、適切なメッセージを使用してユーザーに適切な使用法を通知するとします。
class Protective(object):
"""protected property demo"""
#
def __init__(self, start_protected_value=0):
self.protected_value = start_protected_value
#
@property
def protected_value(self):
return self._protected_value
#
@protected_value.setter
def protected_value(self, value):
if value != int(value):
raise TypeError("protected_value must be an integer")
if 0 <= value <= 100:
self._protected_value = int(value)
else:
raise ValueError("protected_value must be " +
"between 0 and 100 inclusive")
#
@protected_value.deleter
def protected_value(self):
raise AttributeError("do not delete, protected_value can be set to 0")
(を__init__
参照してself.protected_value
いますが、プロパティメソッドはを参照していself._protected_value
ます。これは__init__
、がパブリックAPIを介してプロパティを使用し、「保護」されていることを確認するためです)。
そして使い方:
>>> p1 = Protective(3)
>>> p1.protected_value
3
>>> p1 = Protective(5.0)
>>> p1.protected_value
5
>>> p2 = Protective(-5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in __init__
File "<stdin>", line 15, in protected_value
ValueError: protectected_value must be between 0 and 100 inclusive
>>> p1.protected_value = 7.3
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 17, in protected_value
TypeError: protected_value must be an integer
>>> p1.protected_value = 101
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 15, in protected_value
ValueError: protectected_value must be between 0 and 100 inclusive
>>> del p1.protected_value
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 18, in protected_value
AttributeError: do not delete, protected_value can be set to 0
はい、そうです。.setter
そして.deleter
元のプロパティのコピーを作成します。これにより、サブクラスは親の動作を変更せずに動作を適切に変更できます。
class Obj:
"""property demo"""
#
@property
def get_only(self):
return self._attribute
#
@get_only.setter
def get_or_set(self, value):
self._attribute = value
#
@get_or_set.deleter
def get_set_or_delete(self):
del self._attribute
これが機能するためには、それぞれの名前を使用する必要があります。
obj = Obj()
# obj.get_only = 'value' # would error
obj.get_or_set = 'value'
obj.get_set_or_delete = 'new value'
the_value = obj.get_only
del obj.get_set_or_delete
# del obj.get_or_set # would error
これがどこで役立つかわかりませんが、使用、取得、設定、削除のみのプロパティが必要な場合です。おそらく、同じ名前の意味的に同じプロパティに固執するのが最善です。
単純な属性から始めます。
設定、取得、削除に関する機能が後で必要になった場合は、プロパティデコレータを使用して追加できます。
set_...
and という名前の関数は避けてくださいget_...
。それがプロパティの目的です。
__init__
メソッドはをself.protected_value
参照していself._protected_value
ますが、ゲッターとセッターはを参照しています。これがどのように機能するか説明していただけますか?私はあなたのコードをテストしました、そしてそれは現状のまま動作します-したがってこれはタイプミスではありません。
__init__
でしょうか。
self.protected_value = start_protected_value
実際にはセッター関数を呼び出していることに気付きませんでした。宿題だと思いました。
In [1]: class test(object):
def __init__(self):
self.pants = 'pants'
@property
def p(self):
return self.pants
@p.setter
def p(self, value):
self.pants = value * 2
....:
In [2]: t = test()
In [3]: t.p
Out[3]: 'pants'
In [4]: t.p = 10
In [5]: t.p
Out[5]: 20
使用する@property
と@attribute.setter
、あなたは「神託」の方法を使用するだけでなく、オブジェクトを作成し、それを変更するときながら、両方の属性の有効性を確認するのに役立ちます。
class Person(object):
def __init__(self, p_name=None):
self.name = p_name
@property
def name(self):
return self._name
@name.setter
def name(self, new_name):
if type(new_name) == str: #type checking for name property
self._name = new_name
else:
raise Exception("Invalid value for name")
これにより、実際に_name
クライアント開発者から属性を「非表示」にし、名前プロパティタイプのチェックも実行します。開始時でもこのアプローチに従うと、セッターが呼び出されます。そう:
p = Person(12)
につながる:
Exception: Invalid value for name
だが:
>>>p = person('Mike')
>>>print(p.name)
Mike
>>>p.name = 'George'
>>>print(p.name)
George
>>>p.name = 2.3 # Causes an exception
@property
デコレータを確認してください。
アクセサ/ミューテータ(つまり@attr.setter
and @property
)を使用してもしなくてもかまいませんが、最も重要なことは一貫性を保つことです!
@property
単に属性にアクセスするために使用している場合、例えば
class myClass:
def __init__(a):
self._a = a
@property
def a(self):
return self._a
すべての*属性にアクセスするためにそれを使用してください!アクセサなしで@property
他のいくつかのプロパティを使用してパブリック(つまり、アンダースコアなしの名前)のままにすることは、悪い例です。
class myClass:
def __init__(a, b):
self.a = a
self.b = b
@property
def a(self):
return self.a
self.b
パブリックであっても、ここには明示的なアクセサがないことに注意してください。
同様に、セッター(またはミューテーター)でも、自由に使用できます@attribute.setter
が、一貫性があります。あなたがするとき
class myClass:
def __init__(a, b):
self.a = a
self.b = b
@a.setter
def a(self, value):
return self.a = value
私が君の意図を推測するのは難しい。一方で、a
とb
は両方ともパブリック(名前の先頭にアンダースコアがない)であるため、理論的には両方にアクセス/変更(取得/設定)できるようにする必要があります。しかし、それから明示的なミューテーターのみを指定すると、a
おそらく設定できないはずb
です。明示的なミューテーターを提供したので、明示的なアクセサー(@property
)がないということは、これらの変数のいずれにもアクセスできないはずか、単にを使用することに質素だったのかわかりません@property
。
*例外は、一部の変数を明示的にアクセス可能または変更可能にしたいが両方を変更したくない場合、または属性にアクセスまたは変更するときに追加のロジックを実行したい場合です。これは私が個人的に@property
and を使用している場合です@attribute.setter
(そうでない場合、公開属性の明示的なアクセス権/ミューテーターはありません)。
最後に、PEP8とGoogleスタイルガイドの提案:
PEP8、Designing for Inheritanceは言う:
単純なパブリックデータ属性の場合、複雑なアクセサー/ミューテーターメソッドを使用せずに、属性名のみを公開するのが最善です。単純なデータ属性が機能的な動作を拡張する必要があることがわかった場合、Pythonは将来の拡張への簡単なパスを提供することに注意してください。その場合は、プロパティを使用して、機能の実装を単純なデータ属性アクセス構文の背後に隠します。
一方、GoogleスタイルガイドのPython言語のルール/プロパティによると、次のことをお勧めします。
新しいコードのプロパティを使用して、通常はシンプルで軽量なアクセサーまたはセッターメソッドを使用していたデータにアクセスまたは設定します。プロパティは、
@property
デコレータを使用して作成する必要があります。
このアプローチの長所:
単純な属性アクセスのための明示的なgetおよびsetメソッド呼び出しを排除することにより、可読性が向上します。計算を遅延させることができます。クラスのインターフェースを維持するためのPython的な方法を検討しました。パフォーマンスの面では、直接変数アクセスが妥当な場合、プロパティを許可すると、簡単なアクセサーメソッドが必要なくなります。これにより、インターフェイスを壊すことなく、将来的にアクセサメソッドを追加することもできます。
と短所:
object
Python 2 から継承する必要があります。演算子のオーバーロードのように副作用を隠すことができます。サブクラスでは混乱する可能性があります。
@property
残りの属性も使用するの@property
は適切ではないようです。
@property
(たとえば、属性を返す前に特別なロジックを実行するなど)。それ以外の場合、なぜ1つの属性を@propery
他の属性ではなく装飾するのですか?
@property
してはいけませんよね?getterがreturn this._x
で、setterがthis._x = new_x
である場合、使用@property
するのはちょっとばかげています。
@property
は一貫している」と言ったと思います。
魔法のメソッド__getattribute__
とを使用できます__setattr__
。
class MyClass:
def __init__(self, attrvalue):
self.myattr = attrvalue
def __getattribute__(self, attr):
if attr == "myattr":
#Getter for myattr
def __setattr__(self, attr):
if attr == "myattr":
#Setter for myattr
あることに注意してください__getattr__
とは__getattribute__
同じではありません。__getattr__
属性が見つからない場合にのみ呼び出されます。