__get__と__set__およびPython記述子について


310

私はPythonの記述子が何であり、それらが何に役立つのかを理解しようとしています。私はそれらがどのように機能するか理解していますが、ここに私の疑問があります。次のコードを検討してください。

class Celsius(object):
    def __init__(self, value=0.0):
        self.value = float(value)
    def __get__(self, instance, owner):
        return self.value
    def __set__(self, instance, value):
        self.value = float(value)


class Temperature(object):
    celsius = Celsius()
  1. 記述子クラスが必要なのはなぜですか?

  2. 何であるinstanceownerここに?(で__get__)。これらのパラメーターの目的は何ですか?

  3. この例をどのように呼び出し/使用しますか?

回答:


147

記述子は、Pythonのproperty型がどのように実装されるかを示します。記述子は単に、などを実装し__get____set__その定義で別のクラスに追加されます(Temperatureクラスで上で行ったように)。例えば:

temp=Temperature()
temp.celsius #calls celsius.__get__

記述子を割り当てたプロパティ(celsius上記の例)にアクセスすると、適切な記述子メソッドが呼び出されます。

instance__get__クラスのインスタンスである(したがって、上記__get__受信するtempが、ownerそれがあろうように、(記述子クラスですTemperature)。

記述子クラスを使用して、それを駆動するロジックをカプセル化する必要があります。このようにして、記述子が高価な操作(たとえば)のキャッシュに使用された場合、そのクラスではなくそれ自体に値を格納できます。

記述子に関する記事はここにあります

編集:コメントでjchlが指摘したように、単に試してみるとTemperature.celsius、そうinstanceなりますNone


6
違いは何だselfとはinstance
Lemma Prism

2
「インスタンス」は任意のクラスのインスタンスにすることができ、自己は同じクラスのインスタンスになります。
TheBeginner

3
@LemmaPrism selfは記述子インスタンスでinstanceあり、記述子が存在する(インスタンス化されている場合)クラスのインスタンスです(instance.__class__ is owner)。
Tcll

Temperature.celsius0.0コードに従って値を与えますcelsius = Celsius()。記述子摂氏が呼び出されるので、そのインスタンスには0.0、温度クラス属性摂氏に割り当てられた初期値があります。
Angel Salazar

109

記述子クラスが必要なのはなぜですか?

これにより、属性の動作をさらに細かく制御できます。たとえば、Javaでゲッターとセッターに慣れている場合、それはPythonの方法です。1つの利点は、属性のようにユーザーに見えることです(構文に変更はありません)。したがって、通常の属性から始めて、何か特別なことをする必要がある場合は、記述子に切り替えることができます。

属性は単なる変更可能な値です。記述子を使用すると、値の読み取りまたは設定(または削除)時に任意のコードを実行できます。したがって、たとえば、ORMのようなデータベースのフィールドに属性をマップするためにそれを使用することを想像できます。

別の用途は、例外をスローすることによって新しい値を受け入れることを拒否することです__set__–効果的に「属性」を読み取り専用にすること。

何であるinstanceownerここに?(で__get__)。これらのパラメーターの目的は何ですか?

これはかなり微妙です(そして、ここで新しい答えを書いている理由-同じことを考えながらこの質問を見つけましたが、既存の答えはそれほど見つかりませんでした)。

記述子はクラスで定義されますが、通常はインスタンスから呼び出されます。これは、インスタンスの両方から呼ばれていた場合instanceowner設定されている(そして、あなたは出て働くことができるownerからinstance、それはちょっと無意味と思われるので)。しかし、クラスから呼び出された場合ownerは、設定されるだけです–そのため、そこにあります。

これだけのために必要である__get__ことは、クラスで呼び出すことができる唯一の一つだからです。クラス値を設定する場合は、記述子自体を設定します。削除についても同様です。これが、ownerそこに必要がない理由です。

この例をどのように呼び出し/使用しますか?

まあ、これは同様のクラスを使用したクールなトリックです:

class Celsius:

    def __get__(self, instance, owner):
        return 5 * (instance.fahrenheit - 32) / 9

    def __set__(self, instance, value):
        instance.fahrenheit = 32 + 9 * value / 5


class Temperature:

    celsius = Celsius()

    def __init__(self, initial_f):
        self.fahrenheit = initial_f


t = Temperature(212)
print(t.celsius)
t.celsius = 0
print(t.fahrenheit)

(私はPython 3を使用しています。python2の場合、これらの分割がであることを確認する必要が/ 5.0あります/ 9.0)。それは与える:

100.0
32.0

今、Pythonで同じ効果を達成するための間違いなくより良い方法が他にもあります(たとえば、摂氏がプロパティである場合、これは同じ基本メカニズムですが、すべてのソースをTemperatureクラス内に配置します)が、これは何ができるかを示しています...


2
変換は正しくありません。C= 5(F−32)/ 9、F = 32 + 9C / 5である必要があります。
musiphil 2015年

1
温度のオブジェクトが1つあることを確認してください。次のことを行うと、混乱を招きます。t1 = Temperature(190)print t1.celsius t1.celsius = 100 print t1.fahrenheit t.celciusとt.fahrenheitをチェックすると、これらも変更されます。t.celciusは115でt.fahrenheitは32です。これは明らかに間違っています。@Eric
Bhatt

1
@IshanBhatt:それは、上記のmusiphilによって指摘されたエラーが原因だと思います。また、これは私の答えではありません
Eric

69

私はPythonの記述子が何であり、それらが何に役立つのかを理解しようとしています。

記述子は、次の特別なメソッドのいずれかを持つクラス属性(プロパティやメソッドなど)です。

  • __get__ (メソッド/関数などの非データ記述子メソッド)
  • __set__ (たとえば、プロパティインスタンスのデータ記述子メソッド)
  • __delete__ (データ記述子メソッド)

これらの記述子オブジェクトは、他のオブジェクトクラス定義の属性として使用できます。(つまり、それら__dict__はクラスオブジェクトのにあります。)

記述子オブジェクトを使用するfoo.descriptorと、通常の式、割り当て、および削除でさえも、ドット付きルックアップの結果(など)をプログラムで管理できます。

関数/メソッド、バウンド方法、propertyclassmethod、とstaticmethod彼らは点線の検索を介してアクセスする方法を制御するために、すべての使用これらの特別な方法。

のようなデータ記述子propertyを使用すると、オブジェクトのより単純な状態に基づいて属性の遅延評価が可能になり、可能な各属性を事前に計算した場合よりもインスタンスが使用するメモリが少なくなります。

別のデータ記述子aはmember_descriptor、によって作成され__slots__、クラスがより柔軟でスペースを消費するのではなく、可変タプルのようなデータ構造にデータを格納できるようにすることで、メモリを節約できます__dict__

非データディスクリプタは、通常、例えば、クラス、および静的メソッド、彼らの暗黙の最初の引数(通常は名前の取得clsself、その非データ記述方法から、それぞれを、) __get__

Pythonのほとんどのユーザーは、簡単な使用法のみを学習する必要があり、記述子の実装をさらに学習または理解する必要はありません。

詳細:ディスクリプタとは何ですか?

記述子は、以下の方法のいずれかを持つオブジェクトである(__get____set__または__delete__)、それがインスタンスの一般的な属性であるかのように点線のルックアップを介して使用されることを意図。所有者オブジェクトの、obj_instancedescriptorオブジェクト。

  • obj_instance.descriptor呼び出す
    descriptor.__get__(self, obj_instance, owner_class)返すvalue
    これはどのようにすべてのメソッドで、get不動産の仕事に。

  • obj_instance.descriptor = value呼び出す
    descriptor.__set__(self, obj_instance, value)返すNone
    これはどのようにあるsetterプロパティ作品に。

  • del obj_instance.descriptor呼び出す
    descriptor.__delete__(self, obj_instance)返すNone
    これはどのようにあるdeleterプロパティ作品に。

obj_instanceクラスが記述子オブジェクトのインスタンスを含むインスタンスです。記述子selfのインスタンスです(おそらくのクラスの1つだけです)obj_instance

これをコードで定義するには、属性のセットが必要な属性のいずれかと交差する場合、オブジェクトは記述子です。

def has_descriptor_attrs(obj):
    return set(['__get__', '__set__', '__delete__']).intersection(dir(obj))

def is_descriptor(obj):
    """obj can be instance of descriptor or the descriptor class"""
    return bool(has_descriptor_attrs(obj))

A データ記述子はあり__set__および/または__delete__
A 非データ記述子は、どちらも持っていない__set____delete__

def has_data_descriptor_attrs(obj):
    return set(['__set__', '__delete__']) & set(dir(obj))

def is_data_descriptor(obj):
    return bool(has_data_descriptor_attrs(obj))

組み込み記述子オブジェクトの例:

  • classmethod
  • staticmethod
  • property
  • 一般的な機能

非データ記述子

これを見ることができclassmethodstaticmethod非データ記述子です。

>>> is_descriptor(classmethod), is_data_descriptor(classmethod)
(True, False)
>>> is_descriptor(staticmethod), is_data_descriptor(staticmethod)
(True, False)

どちらも__get__メソッドしかありません:

>>> has_descriptor_attrs(classmethod), has_descriptor_attrs(staticmethod)
(set(['__get__']), set(['__get__']))

すべての関数は非データ記述子でもあることに注意してください。

>>> def foo(): pass
... 
>>> is_descriptor(foo), is_data_descriptor(foo)
(True, False)

データ記述子、 property

ただし、propertyデータ記述子は次のとおりです。

>>> is_data_descriptor(property)
True
>>> has_descriptor_attrs(property)
set(['__set__', '__get__', '__delete__'])

点線のルックアップ順序

これらは、ドット付きルックアップのルックアップ順序に影響を与えるため、重要な違いです。

obj_instance.attribute
  1. まず、上記は属性がインスタンスのクラスのデータ記述子であるかどうかを確認します。
  2. そうでない場合、それは属性がであるかどうかを確認するために見えるobj_instance__dict__は、
  3. 最終的に非データ記述子にフォールバックします。

この検索順序の結果は、関数/メソッドなどの非データ記述子がインスタンスによってオーバーライドされる可能性があることです。

要約と次のステップ

私たちは、記述子のいずれかを持つオブジェクトであることを学んできた__get____set__または__delete__。これらの記述子オブジェクトは、他のオブジェクトクラス定義の属性として使用できます。次に、コードを例として使用して、それらの使用方法を確認します。


質問からのコードの分析

ここにあなたのコードがあり、それぞれにあなたの質問と答えが続きます:

class Celsius(object):
    def __init__(self, value=0.0):
        self.value = float(value)
    def __get__(self, instance, owner):
        return self.value
    def __set__(self, instance, value):
        self.value = float(value)

class Temperature(object):
    celsius = Celsius()
  1. 記述子クラスが必要なのはなぜですか?

記述子により、このクラス属性のfloatが常にあり、属性を削除するためにTemperature使用できないことが保証されますdel

>>> t1 = Temperature()
>>> del t1.celsius
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: __delete__

それ以外の場合、記述子は所有者のクラスと所有者のインスタンスを無視し、代わりに記述子に状態を保存します。単純なクラス属性を使用して、すべてのインスタンス間で状態を同じように簡単に共有できます(常にクラスにフロートとして設定し、それを削除しないか、またはそうすることでコードのユーザーに慣れている場合)。

class Temperature(object):
    celsius = 0.0

これにより、例とまったく同じ動作が得られますが(下記の質問3の回答を参照)、Pythonの組み込み(property)が使用され、より慣用的と見なされます。

class Temperature(object):
    _celsius = 0.0
    @property
    def celsius(self):
        return type(self)._celsius
    @celsius.setter
    def celsius(self, value):
        type(self)._celsius = float(value)
  1. ここでインスタンスと所有者は何ですか?(getで)。これらのパラメーターの目的は何ですか?

instance記述子を呼び出している所有者のインスタンスです。所有者は、記述子オブジェクトを使用してデータポイントへのアクセスを管理するクラスです。より説明的な変数名については、この回答の最初の段落の横にある記述子を定義する特別なメソッドの説明を参照してください。

  1. この例をどのように呼び出し/使用しますか?

ここにデモがあります:

>>> t1 = Temperature()
>>> t1.celsius
0.0
>>> t1.celsius = 1
>>> 
>>> t1.celsius
1.0
>>> t2 = Temperature()
>>> t2.celsius
1.0

次の属性は削除できません。

>>> del t2.celsius
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: __delete__

また、floatに変換できない変数を割り当てることはできません。

>>> t1.celsius = '0x02'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 7, in __set__
ValueError: invalid literal for float(): 0x02

それ以外の場合、ここにあるのはすべてのインスタンスのグローバル状態であり、インスタンスに割り当てることによって管理されます。

ほとんどの経験豊富なPythonプログラマーがこの結果を達成するために期待される方法は、propertyデコレーターを使用することです。これは、内部で同じ記述子を使用しますが、所有者クラスの実装に動作をもたらします(これも、上記で定義したとおりです)。

class Temperature(object):
    _celsius = 0.0
    @property
    def celsius(self):
        return type(self)._celsius
    @celsius.setter
    def celsius(self, value):
        type(self)._celsius = float(value)

これは、元のコードとまったく同じ動作を期待します。

>>> t1 = Temperature()
>>> t2 = Temperature()
>>> t1.celsius
0.0
>>> t1.celsius = 1.0
>>> t2.celsius
1.0
>>> del t1.celsius
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: can't delete attribute
>>> t1.celsius = '0x02'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 8, in celsius
ValueError: invalid literal for float(): 0x02

結論

記述子を定義する属性、データ記述子と非データ記述子の違い、それらを使用する組み込みオブジェクト、および使用に関する特定の質問について説明しました。

もう一度、質問の例をどのように使用しますか?私はあなたがそうしないことを望みます。最初の提案(単純なクラス属性)から始めて、必要と思われる場合は2番目の提案(プロパティデコレータ)に進んでください。


1
いいですね、私はこの回答から最も多くを学びました(確かに他の人からも学びました)。この声明についての質問「ほとんどの経験豊富なPythonプログラマーがこの結果を達成するための予想される方法...」。ステートメントの前後で定義するTemeperatureクラスは同じです。あなたがここで得ているものを見逃しましたか?
Yolo Voe

1
@YoloVoeいいえ、そうです。上記の繰り返しであることを強調するために、括弧で囲まれた言い回しを追加しました。
アーロンホール

1
これは驚くべき答えです。私はそれをさらに数回読む必要があるでしょうが、Pythonの私の理解がいくつかのノッチを上げたように感じます
Lucas Young

20

記述子の詳細に入る前に、Pythonの属性ルックアップがどのように機能するかを知っておくことが重要な場合があります。これは、クラスにメタクラスがなく、デフォルトの実装を使用していることを前提としています__getattribute__(どちらも動作の「カスタマイズ」に使用できます)。

この場合の(Python 3.xまたはPython 2.xの新しいスタイルのクラスの)属性ルックアップの最も良い例は、Pythonメタクラスの理解(ionelのコードログ)からのものです。画像は:「カスタマイズ不可能な属性ルックアップ」の代わりとして使用されます。

これは、属性の参照を表すfoobarinstanceのをClass

ここに画像の説明を入力してください

ここでは2つの条件が重要です。

  • クラスがいる場合instance、属性名のエントリを持っており、それが持っている__get____set__
  • に属性名のエントリinstanceないが、クラスにはエントリがあり、その場合__get__

ここに記述子が入ります。

  • __get__との両方を持つデータ記述子__set__
  • のみを持つ非データ記述子__get__

どちらの場合でも、戻り値は__get__、インスタンスを最初の引数として、クラスを2番目の引数として呼び出されます。

ルックアップは、クラス属性ルックアップの場合はさらに複雑です(たとえば、(上記のブログの)クラス属性ルックアップを参照)。

特定の質問に移りましょう:

記述子クラスが必要なのはなぜですか?

ほとんどの場合、記述子クラスを記述する必要はありません。ただし、おそらくごく普通のエンドユーザーです。たとえば関数。関数は記述子です。つまり、関数はself最初の引数として暗黙的に渡されるメソッドとして使用できます。

def test_function(self):
    return self

class TestClass(object):
    def test_method(self):
        ...

test_methodインスタンスを検索すると、「バインドされたメソッド」が返されます。

>>> instance = TestClass()
>>> instance.test_method
<bound method TestClass.test_method of <__main__.TestClass object at ...>>

同様に、__get__メソッドを手動で呼び出すことによって関数をバインドすることもできます(説明のためだけに、実際にはお勧めしません)。

>>> test_function.__get__(instance, TestClass)
<bound method test_function of <__main__.TestClass object at ...>>

この「自己バインドメソッド」を呼び出すこともできます。

>>> test_function.__get__(instance, TestClass)()
<__main__.TestClass at ...>

引数を指定せず、関数がバインドしたインスタンスを返すことに注意してください!

関数は非データ記述子です!

データ記述子のいくつかの組み込み例はpropertyです。無視gettersetterおよび(から記述子があるディスクリプタの手引きガイド「プロパティ」):deleterproperty

class Property(object):
    def __init__(self, fget=None, fset=None, fdel=None, doc=None):
        self.fget = fget
        self.fset = fset
        self.fdel = fdel
        if doc is None and fget is not None:
            doc = fget.__doc__
        self.__doc__ = doc

    def __get__(self, obj, objtype=None):
        if obj is None:
            return self
        if self.fget is None:
            raise AttributeError("unreadable attribute")
        return self.fget(obj)

    def __set__(self, obj, value):
        if self.fset is None:
            raise AttributeError("can't set attribute")
        self.fset(obj, value)

    def __delete__(self, obj):
        if self.fdel is None:
            raise AttributeError("can't delete attribute")
        self.fdel(obj)

それはデータ記述子ですので、あなたがの「名前」見上げるたび、それが呼び出されますproperty、それは単に飾ら機能への代表団@property@name.setterなどを@name.deleter(存在する場合)。

例えば標準ライブラリ内のいくつかの他の記述子がありますstaticmethodclassmethod

記述子のポイントは簡単です(めったに必要としないものの):属性アクセス用の抽象的な共通コード。propertyインスタンス変数アクセスfunctionの抽象化であり、メソッドの抽象化をstaticmethod提供し、インスタンスアクセスを必要としないメソッドの抽象化を提供し、インスタンスアクセスでclassmethodはなくクラスアクセスを必要とするメソッドの抽象化を提供します(これは少し簡略化されています)。

別の例は、クラスプロパティです。

楽しい例の1つ(__set_name__Python 3.6から使用)は、特定のタイプのみを許可するプロパティにすることもできます。

class TypedProperty(object):
    __slots__ = ('_name', '_type')
    def __init__(self, typ):
        self._type = typ

    def __get__(self, instance, klass=None):
        if instance is None:
            return self
        return instance.__dict__[self._name]

    def __set__(self, instance, value):
        if not isinstance(value, self._type):
            raise TypeError(f"Expected class {self._type}, got {type(value)}")
        instance.__dict__[self._name] = value

    def __delete__(self, instance):
        del instance.__dict__[self._name]

    def __set_name__(self, klass, name):
        self._name = name

次に、クラスで記述子を使用できます。

class Test(object):
    int_prop = TypedProperty(int)

そして少し遊んでみます:

>>> t = Test()
>>> t.int_prop = 10
>>> t.int_prop
10

>>> t.int_prop = 20.0
TypeError: Expected class <class 'int'>, got <class 'float'>

または「怠惰なプロパティ」:

class LazyProperty(object):
    __slots__ = ('_fget', '_name')
    def __init__(self, fget):
        self._fget = fget

    def __get__(self, instance, klass=None):
        if instance is None:
            return self
        try:
            return instance.__dict__[self._name]
        except KeyError:
            value = self._fget(instance)
            instance.__dict__[self._name] = value
            return value

    def __set_name__(self, klass, name):
        self._name = name

class Test(object):
    @LazyProperty
    def lazy(self):
        print('calculating')
        return 10

>>> t = Test()
>>> t.lazy
calculating
10
>>> t.lazy
10

これらは、ロジックを共通の記述子に移動することが理にかなっている場合ですが、他の方法でそれらを解決することもできます(ただし、コードを繰り返すことにより)。

何であるinstanceownerここに?(で__get__)。これらのパラメーターの目的は何ですか?

それは、属性の検索方法によって異なります。インスタンスの属性を検索すると、次のようになります。

  • 2番目の引数は、属性を検索するインスタンスです
  • 3番目の引数はインスタンスのクラスです

クラスの属性を検索する場合(記述子がクラスで定義されていると想定):

  • 2番目の引数は None
  • 3番目の引数は、属性を検索するクラスです

したがって、基本的に3番目の引数は、クラスレベルのルックアップを行うときの動作をカスタマイズする場合に必要です(instanceisであるためNone)。

この例をどのように呼び出し/使用しますか?

あなたの例は基本的に変換できる値のみを許可するプロパティでfloatあり、それはクラスのすべてのインスタンス間で共有されます(ただし、クラスで「読み取り」アクセスしか使用できませんが、それ以外の場合は記述子インスタンスを置き換えます) ):

>>> t1 = Temperature()
>>> t2 = Temperature()

>>> t1.celsius = 20   # setting it on one instance
>>> t2.celsius        # looking it up on another instance
20.0

>>> Temperature.celsius  # looking it up on the class
20.0

そのため、記述子は通常、2番目の引数(instance)を使用して値を格納し、共有しないようにします。ただし、インスタンス間で値を共有することが望ましい場合もあります(現時点ではシナリオを考えることはできません)。ただし、温度クラスの摂氏プロパティでは、実質的には意味がありません。


暗いモードで本当に苦しんでいるグラフィックの透明な背景を、stackoverflowのバグとして報告する必要があるかどうかは不明です。
Tシャツマン

@Tshirtmanこれは画像自体の問題だと思います。それは完全に透明ではありません...私はそれをブログの投稿から取り、適切な透明な背景でそれを再作成する方法を知りません。暗い背景で奇妙に見えます:(
MSeifert

9

記述子クラスが必要なのはなぜですか?

Buciano RamalhoによるFluent Pythonに触発されました

あなたはこのようなクラスを持っているイメージング

class LineItem:
     price = 10.9
     weight = 2.1
     def __init__(self, name, price, weight):
          self.name = name
          self.price = price
          self.weight = weight

item = LineItem("apple", 2.9, 2.1)
item.price = -0.9  # it's price is negative, you need to refund to your customer even you delivered the apple :(
item.weight = -0.8 # negative weight, it doesn't make sense

負の数が割り当てられないように、重量と価格を検証する必要があります。このようにプロキシとして記述子を使用すると、より少ないコードを書くことができます

class Quantity(object):
    __index = 0

    def __init__(self):
        self.__index = self.__class__.__index
        self._storage_name = "quantity#{}".format(self.__index)
        self.__class__.__index += 1

    def __set__(self, instance, value):
        if value > 0:
            setattr(instance, self._storage_name, value)
        else:
           raise ValueError('value should >0')

   def __get__(self, instance, owner):
        return getattr(instance, self._storage_name)

次に、クラスLineItemを次のように定義します。

class LineItem(object):
     weight = Quantity()
     price = Quantity()

     def __init__(self, name, weight, price):
         self.name = name
         self.weight = weight
         self.price = price

そしてQuantityクラスを拡張して、より一般的な検証を行うことができます


1
記述子を使用してユーザーの複数のインスタンスとやり取りする方法を示す興味深いユースケース。私は最初に重要なポイントを理解していませんでした:ディスクリプター付きの属性はクラスの名前空間に作成する必要あります(たとえばweight = Quantity()、値はself(たとえばself.weight = 4)を使用してのみインスタンスの名前空間に設定する必要があります。そうしないと、属性は新しい値に再バインドされます記述子は破棄されます。ニース!
2017

一つ理解できません。あなたは定義しているweight = Quantity()クラス変数として、その__get____set__インスタンス変数に取り組んでいます。どうやって?
テクノクラート

0

Andrew Cookeの回答からのコードを(提案されたようにマイナーな変更を加えて)試しました。(私はpython 2.7を実行しています)。

コード:

#!/usr/bin/env python
class Celsius:
    def __get__(self, instance, owner): return 9 * (instance.fahrenheit + 32) / 5.0
    def __set__(self, instance, value): instance.fahrenheit = 32 + 5 * value / 9.0

class Temperature:
    def __init__(self, initial_f): self.fahrenheit = initial_f
    celsius = Celsius()

if __name__ == "__main__":

    t = Temperature(212)
    print(t.celsius)
    t.celsius = 0
    print(t.fahrenheit)

結果:

C:\Users\gkuhn\Desktop>python test2.py
<__main__.Celsius instance at 0x02E95A80>
212

3より前のPythonでは、getマジックが古いスタイルのクラスでは機能しないため、記述子を正しく機能させるオブジェクトからサブクラスを作成してください。


1
記述子は新しいスタイルクラスでのみ機能します。Python用のPython 3でのデフォルトである「オブジェクト」、からクラスを派生この手段から2.x
Wijk DERイヴォバンを

0

https://docs.python.org/3/howto/descriptor.html#propertiesが表示されます

class Property(object):
    "Emulate PyProperty_Type() in Objects/descrobject.c"

    def __init__(self, fget=None, fset=None, fdel=None, doc=None):
        self.fget = fget
        self.fset = fset
        self.fdel = fdel
        if doc is None and fget is not None:
            doc = fget.__doc__
        self.__doc__ = doc

    def __get__(self, obj, objtype=None):
        if obj is None:
            return self
        if self.fget is None:
            raise AttributeError("unreadable attribute")
        return self.fget(obj)

    def __set__(self, obj, value):
        if self.fset is None:
            raise AttributeError("can't set attribute")
        self.fset(obj, value)

    def __delete__(self, obj):
        if self.fdel is None:
            raise AttributeError("can't delete attribute")
        self.fdel(obj)

    def getter(self, fget):
        return type(self)(fget, self.fset, self.fdel, self.__doc__)

    def setter(self, fset):
        return type(self)(self.fget, fset, self.fdel, self.__doc__)

    def deleter(self, fdel):
        return type(self)(self.fget, self.fset, fdel, self.__doc__)

1
これは質問に答えたり、有用な情報を提供したりしません。
セバスチャンニールセン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.