web-dev-qa-db-ja.com

ゲッターとセッターを使用するPythonicの方法は何ですか?

私はそれをやっています:

def set_property(property,value):  
def get_property(property):  

または

object.property = value  
value = object.property

私はPythonが初めてなので、まだ構文を調べています。そのためにはアドバイスをお願いします。

271
Jorge Guberte

これを試してください。 Python Property

サンプルコードは次のとおりです。

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
602
Grissiom

ゲッターとセッターを使用するPythonicの方法は何ですか?

"Pythonic"の方法はではない "getters"と "setters"を使うことですが、質問が示すように普通の属性を使い、delを参照解除にします(ただし、名前は無実を保護するために変更されます)。 .. builtins):

value = 'something'

obj.attribute = value  
value = obj.attribute
del obj.attribute

後で設定を変更して取得したい場合は、propertyデコレータを使用して、ユーザーコードを変更しなくても変更できます。

class Obj:
    """property demo"""
    #
    @property
    def attribute(self): # implements the get - this name is *the* name
        return self._attribute
    #
    @attribute.setter
    def attribute(self, value): # name must be the same
        self._attribute = value
    #
    @attribute.deleter
    def attribute(self): # again, name must be the same
        del self._attribute

(各デコレータは前のプロパティオブジェクトをコピーして更新します。したがって、各関数のset、get、およびdeleteにはおそらく同じ名前を使用する必要があります。)

上記を定義した後、元の設定、取得、削除は同じです。

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
        ...

第二に、これは2つの特別なメソッド、__setattr____getattr__の目的を複製します。

第三に、setattrgetattr組み込み関数もあります。

    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のユーザーに期待されていることです。あなたが反対に非常に説得力のある理由を持っていない限り、最小の驚きのルールに従って、あなたはあなたのユーザーに彼らが期待するものを与えるようにするべきです。

デモンストレーション

たとえば、オブジェクトのprotected属性に0から100までの整数を指定し、削除を防ぎ、適切な使用法をユーザーに通知するための適切なメッセージを含める必要があるとします。

class Protective(object):
    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")

そして使用法:

>>> 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

名前は重要ですか?

はい、彼らはdoです。 .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

これがどこで役に立つのかよくわかりませんが、get、set、またはdelete-onlyプロパティが必要な場合がユースケースです。同じ名前を持つ意味的に同じプロパティに固執するのが最も良いでしょう。

結論

単純な属性から始めましょう。

後で設定、取得、削除に関する機能が必要になった場合は、プロパティデコレータを使って追加できます。

set_...get_...という名前の関数は避けてください。それがプロパティの目的です。

183
Aaron Hall
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
25
Autoplectic

@propertyデコレータ を調べてください。

16
Kevin Little

@propertyおよび@attribute.setterを使用することは、「Pythonic」方法を使用するだけでなく、オブジェクトを作成している間およびそれを変更するときの両方で属性の有効性をチェックするのに役立ちます。

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属性を「隠し」、また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
10
Farzad Vertigo

アクセサー/ミューテーター(つまり、@attr.setterおよび@property)を使用しても使用しなくてもかまいませんが、最も重要なことは一貫性を保つために!

PEP8継承のための設計 言います:

単純なパブリックデータ属性の場合、複雑なアクセサー/ミューテーターメソッドを使用せずに、属性名のみを公開するのが最適です。 Pythonは、単純なデータ属性が機能的な動作を成長させる必要があることがわかった場合に、将来の拡張への簡単なパスを提供することに留意してください。その場合、プロパティを使用して、単純なデータ属性アクセス構文の背後に機能実装を隠します。

一方、GoogleスタイルガイドPython言語ルール/プロパティ によると、推奨事項は次のとおりです。

新しいコードでプロパティを使用して、通常シンプルで軽量なアクセサーまたはセッターメソッドを使用するデータにアクセスまたは設定します。プロパティは、@propertyデコレータで作成する必要があります。

このアプローチの長所:

単純な属性アクセスのための明示的なgetおよびsetメソッド呼び出しを排除することにより、可読性が向上します。計算を遅延させることができます。クラスのインターフェイスを維持するPythonの方法を検討しました。パフォーマンスの観点から、直接変数アクセスが合理的である場合、プロパティを許可すると、簡単なアクセサメソッドが必要になりません。また、これにより、今後インターフェースを壊さずにアクセサーメソッドを追加できます。

と短所:

Pythonのobjectを継承する必要があります。2.演算子のオーバーロードと同様に副作用を隠すことができます。サブクラスを混乱させる可能性があります。

1